{
  "cells": [
    {
      "cell_type": "code",
      "execution_count": 36,
      "metadata": {
        "executionInfo": {
          "elapsed": 52,
          "status": "ok",
          "timestamp": 1658264802715,
          "user": {
            "displayName": "",
            "userId": ""
          },
          "user_tz": 240
        },
        "id": "qfVq685nRfsc"
      },
      "outputs": [],
      "source": [
        "# Copyright 2022 Google LLC\n",
        "#\n",
        "# Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "#      http://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "CE1g_AzREAgQ"
      },
      "source": [
        "# Correlated compression experiments\n",
        "\n",
        "[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/google-research/google-research/blob/master/correlated_compression/correlated_compression_experiments.ipynb)\n",
        "\n",
        "This notebook contains the experiments run for the paper [\"Correlated quantization for distributed mean estimation and optimization\"](https://arxiv.org/abs/2203.04925)."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 37,
      "metadata": {
        "executionInfo": {
          "elapsed": 2,
          "status": "ok",
          "timestamp": 1658264802845,
          "user": {
            "displayName": "",
            "userId": ""
          },
          "user_tz": 240
        },
        "id": "lM6mKQ-KDwpb"
      },
      "outputs": [],
      "source": [
        "# Uncomment this to install fedjax at HEAD.\n",
        "# !pip install --upgrade -q git+https://github.com/google/fedjax.git"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 38,
      "metadata": {
        "executionInfo": {
          "elapsed": 53,
          "status": "ok",
          "timestamp": 1658264803023,
          "user": {
            "displayName": "",
            "userId": ""
          },
          "user_tz": 240
        },
        "id": "QQW7ay60fnHn"
      },
      "outputs": [],
      "source": [
        "import functools\n",
        "import itertools\n",
        "import math\n",
        "import time\n",
        "\n",
        "from typing import Any, Callable, Iterable, Mapping, Optional, Sequence, Tuple\n",
        "\n",
        "import fedjax\n",
        "import haiku as hk\n",
        "import jax\n",
        "import jax.numpy as jnp\n",
        "import matplotlib\n",
        "import matplotlib.pyplot as plt\n",
        "import numpy as np\n",
        "import scipy\n",
        "import tensorflow as tf\n",
        "import tensorflow_datasets as tfds\n",
        "\n",
        "fedjax.training.set_tf_cpu_only()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "wuN3LnL16aF5"
      },
      "source": [
        "## Correlated stochastic quantizer"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 39,
      "metadata": {
        "executionInfo": {
          "elapsed": 286,
          "status": "ok",
          "timestamp": 1658264803445,
          "user": {
            "displayName": "",
            "userId": ""
          },
          "user_tz": 240
        },
        "id": "w8etWDdZ6ZMn"
      },
      "outputs": [],
      "source": [
        "@fedjax.dataclass\n",
        "class CorrelatedCompressionState:\n",
        "  \"\"\"State of correlated compression aggregator passed between rounds.\n",
        "\n",
        "  Attributes:\n",
        "    num_bits: Number of bits transmitted per client.\n",
        "    rng: PRNGKey used for compression.\n",
        "    thresholds: Threshold for each array in the pytree.\n",
        "  \"\"\"\n",
        "  num_bits: float\n",
        "  rng: fedjax.PRNGKey\n",
        "  thresholds: fedjax.Params\n",
        "\n",
        "\n",
        "def correlated_stochastic_quantize(v, num_levels, num_clients, rng, shared_rng,\n",
        "                                   threshold, index, c1):\n",
        "  \"\"\"Applies correlated stochastic quantization on input vector v.\n",
        "\n",
        "  Correlated quantization for distributed mean estimation and optimization\n",
        "    Ananda Theertha Suresh, Ziteng Sun, Jae Hun Ro, Felix Yu\n",
        "\n",
        "  Args:\n",
        "    v: Vector to be quantized.\n",
        "    num_levels: Number of levels of quantization.\n",
        "    num_clients: Number of clients.\n",
        "    rng: PRNGKey.\n",
        "    shared_rng: PRNGKey that is shared across clients per round.\n",
        "    threshold: Clipping threshold.\n",
        "    index: Index of the client.\n",
        "    c1: Initial value c1.\n",
        "\n",
        "  Returns:\n",
        "    Quantized vector and the threshold of v.\n",
        "  \"\"\"\n",
        "  # Rescale the vector to be between zero to one.\n",
        "  output_min = jnp.amin(v)\n",
        "  output_max = jnp.amax(v)\n",
        "  v_min = threshold[0]\n",
        "  v_max = threshold[1]\n",
        "  v = jnp.nan_to_num((v - v_min) / (v_max - v_min))\n",
        "  # Sets v to 0 if v \u003c v_min or 1 if v \u003e v_max.\n",
        "  v = jnp.maximum(0., jnp.minimum(v, 1.))\n",
        "  # Since it is expensive to generate a different shared permutation for each\n",
        "  # coordinate in v for each client, we can work around this by utilizing the\n",
        "  # client permutation index.\n",
        "  # Using the shared_rng will make the coordinate samples the same across\n",
        "  # clients so adding the index will make standard_perm different per client.\n",
        "  # However, the range of values will be [0, 2n - 2] so we modulo by n to set\n",
        "  # it back to be a permutation of [0, n - 1].\n",
        "  standard_perm = jnp.mod(\n",
        "      jax.random.randint(\n",
        "          key=shared_rng, shape=v.shape, minval=0, maxval=num_clients) + index,\n",
        "      num_clients)\n",
        "  # Add random per coordinate per vector per client value to perm.\n",
        "  rand = standard_perm + jax.random.uniform(key=rng, shape=v.shape)\n",
        "  # Scale back to [0, 1].\n",
        "  rand = rand / num_clients\n",
        "  if num_levels == 2:\n",
        "    quantized = jnp.where(rand \u003e v, 0., 1.)\n",
        "  else:\n",
        "    # For \u003e 2 levels, we want to adjust the thresholds so that close values are\n",
        "    # mapped to the same quantization.\n",
        "    # beta is the interval width.\n",
        "    # c1 is smallest quant value and ck is largest.\n",
        "    beta = (num_levels + 1) / (num_levels * (num_levels - 1))\n",
        "    # c1 is the smallest quantization value (roughly -1/k).\n",
        "    # ck is the largest quantization value.\n",
        "    ck = c1 + (num_levels - 1) * beta\n",
        "    # Scaling v to the new interval [c1, ck].\n",
        "    v = (v - c1) / (ck - c1)\n",
        "    # v_ceil and v_floor are the boundaries directly above and below v.\n",
        "    v_ceil = jnp.ceil(v * (num_levels - 1)) / (num_levels - 1)\n",
        "    v_floor = jnp.floor(v * (num_levels - 1)) / (num_levels - 1)\n",
        "    # Scaling threshold between v_ceil and v_floor to determine probability of\n",
        "    # rounding up to v_ceil.\n",
        "    threshold = jnp.nan_to_num((v - v_floor) / (v_ceil - v_floor))\n",
        "    quantized = jnp.where(rand \u003e threshold, v_floor, v_ceil)\n",
        "    # Rescale back to [v_min, v_max].\n",
        "    quantized = c1 + (ck - c1) * quantized\n",
        "  # Rescale the values and return it.\n",
        "  quantized_v = v_min + quantized * (v_max - v_min)\n",
        "  new_threshold = jnp.array([output_min, output_max])\n",
        "  return quantized_v, new_threshold\n",
        "\n",
        "\n",
        "@functools.partial(jax.jit, static_argnums=(1))\n",
        "def correlated_stochastic_quantize_pytree(params, num_levels, num_clients,\n",
        "                                          client_rng, shared_rng, thresholds,\n",
        "                                          index, c1s):\n",
        "  \"\"\"Applies correlated quantization for all leaves of the params pytree.\n",
        "\n",
        "  Args:\n",
        "    params: Pytree of parameters to be quantized.\n",
        "    num_levels: Number of levels of quantization.\n",
        "    num_clients: Number of clients.\n",
        "    client_rng: PRNGKey of client.\n",
        "    shared_rng: PRNGKey that is shared across clients in a round.\n",
        "    thresholds: A pytree of absolute mins and maxes for each array in params.\n",
        "    index: Index of the user.\n",
        "    c1s: Initial value c1s.\n",
        "\n",
        "  Returns:\n",
        "    Quantized params pytree and the pytree of thresholds.\n",
        "  \"\"\"\n",
        "  p_leaves, p_tree_def = jax.tree_util.tree_flatten(params)\n",
        "  thresh_leaves, thresh_tree_def = jax.tree_util.tree_flatten(thresholds)\n",
        "  p_rngs = jax.random.split(client_rng, len(p_leaves))\n",
        "  # Split the shared key so a different key is used for each parameter but is\n",
        "  # still identical across clients.\n",
        "  shared_rngs = jax.random.split(shared_rng, len(p_leaves))\n",
        "  quant_p_leaves = []\n",
        "  new_thresh_leaves = []\n",
        "  for p_leaf, p_rng, s_rng, thresh, c1 in zip(p_leaves, p_rngs, shared_rngs,\n",
        "                                              thresh_leaves, c1s):\n",
        "    quant_p_leaf, new_thresh = correlated_stochastic_quantize(\n",
        "        p_leaf, num_levels, num_clients, p_rng, s_rng, thresh, index, c1)\n",
        "    quant_p_leaves.append(quant_p_leaf)\n",
        "    new_thresh_leaves.append(new_thresh)\n",
        "  quant_params = jax.tree_util.tree_unflatten(p_tree_def, quant_p_leaves)\n",
        "  new_thresholds = jax.tree_util.tree_unflatten(thresh_tree_def,\n",
        "                                                new_thresh_leaves)\n",
        "  return quant_params, new_thresholds\n",
        "\n",
        "\n",
        "def correlated_stochastic_quantizer(\n",
        "    num_levels: int,\n",
        "    rng: fedjax.PRNGKey,\n",
        "    num_clients: int,\n",
        "    thresholds: fedjax.Params,\n",
        "    update_thresholds: bool,\n",
        "    rotate: bool = False) -\u003e fedjax.aggregators.Aggregator:\n",
        "  \"\"\"Returns aggregator for computing (weighted) mean of inputs after correlated quantization.\n",
        "\n",
        "  Correlated quantization for distributed mean estimation and optimization\n",
        "    Ananda Theertha Suresh, Ziteng Sun, Jae Hun Ro, Felix Yu\n",
        "\n",
        "  Args:\n",
        "    num_levels: Number of levels of quantization.\n",
        "    rng: PRNGKey used for compression.\n",
        "    num_clients: Number of clients.\n",
        "    thresholds: A pytree of absolute mins and maxes for each array.\n",
        "    update_thresholds: Whether thresholds should be updated.\n",
        "    rotate: Whether to apply efficient Walsh-Hadamard rotation.\n",
        "\n",
        "  Returns:\n",
        "    Correlated compression aggregator.\n",
        "  \"\"\"\n",
        "\n",
        "  def init():\n",
        "    return CorrelatedCompressionState(\n",
        "        num_bits=0.0, rng=rng, thresholds=thresholds)\n",
        "\n",
        "  def apply(\n",
        "      clients_params_and_weights: Iterable[Tuple[fedjax.ClientId, fedjax.Params,\n",
        "                                                 float]],\n",
        "      aggregator_state: CorrelatedCompressionState\n",
        "  ) -\u003e Tuple[fedjax.Params, CorrelatedCompressionState]:\n",
        "\n",
        "    rng, c1s_rng, perms_rng, hk_rng, rotate_rng, shared_rng = jax.random.split(\n",
        "        aggregator_state.rng, 6)\n",
        "    # c1s are uniformly distributed in the interval [-1/k, 0).\n",
        "    c1s = -jax.random.uniform(\n",
        "        key=c1s_rng,\n",
        "        shape=[fedjax.aggregators.compression.num_leaves(thresholds)\n",
        "              ]) / num_levels\n",
        "    # π random permutation of {0, 1, 2, ..., n - 1}.\n",
        "    pi = jax.random.shuffle(perms_rng, jnp.arange(num_clients))\n",
        "    # Unpack into a single tuple containing (cid, params, weight, rng, index).\n",
        "    clients_params_weight_rng_pis = map(lambda cpw, r, p: (*cpw, r, p),\n",
        "                                        clients_params_and_weights,\n",
        "                                        hk.PRNGSequence(hk_rng), pi)\n",
        "    # Optionally update thresholds by recording max and min across clients while\n",
        "    # streaming for quantization.\n",
        "    new_thresholds_leaves = []\n",
        "    # Convenience array to switch sign of left threshold so we can do a single\n",
        "    # element-wise maximum over both.\n",
        "    lsign = jnp.array([-1.0, 1.0])\n",
        "\n",
        "    def quantize_params(cid, params, weight, rng, index):\n",
        "      del cid\n",
        "      nonlocal new_thresholds_leaves\n",
        "      if rotate:\n",
        "        rotated_params, params_shapes = fedjax.aggregators.walsh_hadamard.structured_rotation_pytree(\n",
        "            params, rotate_rng)\n",
        "        q_rotated_params, thresholds = correlated_stochastic_quantize_pytree(\n",
        "            rotated_params, num_levels, num_clients, rng, shared_rng,\n",
        "            aggregator_state.thresholds, index, c1s)\n",
        "        q_params = fedjax.aggregators.walsh_hadamard.inverse_structured_rotation_pytree(\n",
        "            q_rotated_params, rotate_rng, params_shapes)\n",
        "      else:\n",
        "        q_params, thresholds = correlated_stochastic_quantize_pytree(\n",
        "            params, num_levels, num_clients, rng, shared_rng,\n",
        "            aggregator_state.thresholds, index, c1s)\n",
        "      if update_thresholds:\n",
        "        # Record current min left and max right thresholds for each leaf.\n",
        "        thresholds_leaves = jax.tree_util.tree_leaves(thresholds)\n",
        "        if not new_thresholds_leaves:\n",
        "          new_thresholds_leaves = thresholds_leaves\n",
        "        else:\n",
        "          for i in range(len(thresholds_leaves)):\n",
        "            new_thresholds_leaves[i] = jnp.maximum(\n",
        "                thresholds_leaves[i] * lsign,\n",
        "                new_thresholds_leaves[i] * lsign) * lsign\n",
        "      return q_params, weight\n",
        "\n",
        "    # Quantize parameters and (possibly) generate new thresholds in stream.\n",
        "    q_params_and_weights = itertools.starmap(quantize_params,\n",
        "                                             clients_params_weight_rng_pis)\n",
        "    aggregated_params = fedjax.tree_util.tree_mean(q_params_and_weights)\n",
        "    total_num_params = fedjax.tree_util.tree_size(aggregated_params)\n",
        "    total_num_floats = 2 * fedjax.aggregators.compression.num_leaves(\n",
        "        aggregated_params)\n",
        "    # 32 bits for every float used and one bit for every parameter.\n",
        "    new_bits = total_num_params + 32 * total_num_floats\n",
        "    # Possibly update thresholds.\n",
        "    if update_thresholds:\n",
        "      thresholds_tree_def = jax.tree_util.tree_structure(\n",
        "          aggregator_state.thresholds)\n",
        "      new_thresholds = jax.tree_util.tree_unflatten(thresholds_tree_def,\n",
        "                                                    new_thresholds_leaves)\n",
        "    else:\n",
        "      new_thresholds = aggregator_state.thresholds\n",
        "    new_aggregator_state = CorrelatedCompressionState(\n",
        "        num_bits=aggregator_state.num_bits + new_bits,\n",
        "        rng=rng,\n",
        "        thresholds=new_thresholds)\n",
        "    return aggregated_params, new_aggregator_state\n",
        "\n",
        "  return fedjax.aggregators.Aggregator(init, apply)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "xSd3O3SVvk1o"
      },
      "source": [
        "## Experiment utils"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 40,
      "metadata": {
        "executionInfo": {
          "elapsed": 52,
          "status": "ok",
          "timestamp": 1658264803607,
          "user": {
            "displayName": "",
            "userId": ""
          },
          "user_tz": 240
        },
        "id": "u_7zDICmn_3R"
      },
      "outputs": [],
      "source": [
        "def get_quantizers(rng, num_levels, num_clients, thresholds, update_thresholds):\n",
        "  return {\n",
        "      'None':\n",
        "          fedjax.aggregators.mean_aggregator(),\n",
        "      'Independent':\n",
        "          fedjax.aggregators.compression.uniform_stochastic_quantizer(\n",
        "              num_levels, rng),\n",
        "      'Independent+Rotation':\n",
        "          fedjax.aggregators.compression.rotated_uniform_stochastic_quantizer(\n",
        "              num_levels, rng),\n",
        "      'TernGrad':\n",
        "          fedjax.aggregators.compression.terngrad_quantizer(rng),\n",
        "      'DRIVE':\n",
        "          fedjax.aggregators.compression.structured_drive_quantizer(rng),\n",
        "      'Correlated':\n",
        "          correlated_stochastic_quantizer(num_levels, rng, num_clients,\n",
        "                                          thresholds, update_thresholds),\n",
        "      'Correlated+Rotation':\n",
        "          correlated_stochastic_quantizer(\n",
        "              num_levels,\n",
        "              rng,\n",
        "              num_clients,\n",
        "              thresholds,\n",
        "              update_thresholds,\n",
        "              rotate=True),\n",
        "  }"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 41,
      "metadata": {
        "executionInfo": {
          "elapsed": 54,
          "status": "ok",
          "timestamp": 1658264803767,
          "user": {
            "displayName": "",
            "userId": ""
          },
          "user_tz": 240
        },
        "id": "uGMX_q1gny03"
      },
      "outputs": [],
      "source": [
        "def depends_on_num_levels(quantizer_name):\n",
        "  return quantizer_name not in ('None', 'DRIVE', 'TernGrad')\n",
        "\n",
        "\n",
        "def only_independent_and_correlated(quantizers):\n",
        "  return {\n",
        "      k: v for k, v in quantizers.items() if k in ('Independent', 'Correlated')\n",
        "  }\n",
        "\n",
        "\n",
        "def only_independent_and_correlated_and_rotation(quantizers):\n",
        "  return {\n",
        "      k: v\n",
        "      for k, v in quantizers.items()\n",
        "      if k in ('Independent', 'Correlated', 'Correlated+Rotation',\n",
        "               'Independent+Rotation')\n",
        "  }"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "uILq4xMp0_f_"
      },
      "source": [
        "## Toy Examples"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "-RRqTsj7xXk2"
      },
      "source": [
        "### Correlated vs independent stochastic quantization\n",
        "\n",
        "We first compare correlated and independent stochastic\n",
        "quantizations on a simple mean estimation task.\n",
        "\n",
        "Let x1,x2,...,xn be n i.i.d ̇ samples over R16, where each\n",
        "coordinate is sampled independently according to μ + U,\n",
        "where μ is a uniform random variable between [0,1] and is\n",
        "fixed for all clients and U is a independent random variable\n",
        "for each client in the range −[4σmd,4σmd]. We refer to this\n",
        "distribution as punif. Note that punif has mean deviation of\n",
        "σmd."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 42,
      "metadata": {
        "executionInfo": {
          "elapsed": 52,
          "status": "ok",
          "timestamp": 1658264803973,
          "user": {
            "displayName": "",
            "userId": ""
          },
          "user_tz": 240
        },
        "id": "nXIRZEu6xvY-"
      },
      "outputs": [],
      "source": [
        "@functools.partial(jax.jit, static_argnums=(3, 4))\n",
        "def _sample(rng, mean, sigmamd, dimension, dist):\n",
        "  rng, use_rng = jax.random.split(rng)\n",
        "  if dist == 'punif':\n",
        "    x = mean + 4 * sigmamd * jax.random.uniform(use_rng, shape=[dimension])\n",
        "    return (x, rng)\n",
        "\n",
        "\n",
        "def run_dist_mean(aggregator,\n",
        "                  mean,\n",
        "                  sigmamd,\n",
        "                  num_clients,\n",
        "                  rng,\n",
        "                  dimension=16,\n",
        "                  num_iters=10,\n",
        "                  dist='punif'):\n",
        "  start = time.time()\n",
        "  distances = []\n",
        "  state = aggregator.init()\n",
        "  true_mean_aggregator = fedjax.aggregators.mean_aggregator()\n",
        "  true_mean_empty_state = true_mean_aggregator.init()\n",
        "  for iter_num in range(num_iters + 1):\n",
        "    client_id_x_weights = []\n",
        "    for i in range(num_clients):\n",
        "      x, rng = _sample(rng, mean, sigmamd, dimension, dist)\n",
        "      client_id_x_weights.append((i, x, 1))\n",
        "    true_mean, _ = true_mean_aggregator.apply(client_id_x_weights,\n",
        "                                              true_mean_empty_state)\n",
        "    aggregated_mean, state = aggregator.apply(client_id_x_weights, state)\n",
        "    if iter_num \u003e 0:\n",
        "      # We skip the first iteration, so thresholds can be set correctly for\n",
        "      # all the algorithms.\n",
        "      distance = jnp.linalg.norm(aggregated_mean - true_mean)\n",
        "      distances.append(distance)\n",
        "  print(f'average iteration duration = {(time.time() - start) / num_iters} sec')\n",
        "  return jnp.array(distances)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "21VVEZutzOgp"
      },
      "source": [
        "We first fix the number of clients to be 100, k = 2,\n",
        "and vary σmd"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 43,
      "metadata": {
        "executionInfo": {
          "elapsed": 7811,
          "status": "ok",
          "timestamp": 1658264811906,
          "user": {
            "displayName": "",
            "userId": ""
          },
          "user_tz": 240
        },
        "id": "L6kb95dvzK-h",
        "outputId": "456b1732-3a30-4897-f055-ea9747764063"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "average iteration duration = 0.10913488864898682 sec\n",
            "Finished Independent sigmamd=0.01\n",
            "average iteration duration = 0.16218764781951905 sec\n",
            "Finished Correlated sigmamd=0.01\n",
            "average iteration duration = 0.0569476842880249 sec\n",
            "Finished Independent sigmamd=0.02\n",
            "average iteration duration = 0.06662611961364746 sec\n",
            "Finished Correlated sigmamd=0.02\n",
            "average iteration duration = 0.05702245235443115 sec\n",
            "Finished Independent sigmamd=0.04\n",
            "average iteration duration = 0.06564192771911621 sec\n",
            "Finished Correlated sigmamd=0.04\n",
            "average iteration duration = 0.05737545490264893 sec\n",
            "Finished Independent sigmamd=0.08\n",
            "average iteration duration = 0.06603660583496093 sec\n",
            "Finished Correlated sigmamd=0.08\n",
            "average iteration duration = 0.05976698398590088 sec\n",
            "Finished Independent sigmamd=0.16\n",
            "average iteration duration = 0.06861958503723145 sec\n",
            "Finished Correlated sigmamd=0.16\n"
          ]
        }
      ],
      "source": [
        "def run_vary_sigmamd(seed, sigmamd_range):\n",
        "  rng, mean_rng, quant_rng = jax.random.split(jax.random.PRNGKey(seed), 3)\n",
        "  dimension = 1024\n",
        "  mean = jax.random.uniform(key=mean_rng, shape=[dimension])\n",
        "  num_clients = 100\n",
        "\n",
        "  vary_sigmamd_metrics = {}\n",
        "  for sigmamd in sigmamd_range:\n",
        "    quantizers = get_quantizers(\n",
        "        quant_rng,\n",
        "        num_levels=2,\n",
        "        num_clients=num_clients,\n",
        "        thresholds=jnp.array([0, 1. + 4 * sigmamd]),\n",
        "        update_thresholds=False)\n",
        "    # Just independent and correlated.\n",
        "    quantizers = only_independent_and_correlated(quantizers)\n",
        "    for quantizer_name, quantizer_agg in quantizers.items():\n",
        "      if quantizer_name not in vary_sigmamd_metrics:\n",
        "        vary_sigmamd_metrics[quantizer_name] = {'sigmamd': [], 'distances': []}\n",
        "      vary_sigmamd_metrics[quantizer_name]['sigmamd'].append(sigmamd)\n",
        "      distances = run_dist_mean(\n",
        "          quantizer_agg,\n",
        "          mean,\n",
        "          sigmamd,\n",
        "          num_clients,\n",
        "          rng,\n",
        "          dimension,\n",
        "          num_iters=10)\n",
        "      vary_sigmamd_metrics[quantizer_name]['distances'].append(distances)\n",
        "      print(f'Finished {quantizer_name} sigmamd={sigmamd}')\n",
        "  return vary_sigmamd_metrics\n",
        "\n",
        "\n",
        "vary_sigmamd_metrics = run_vary_sigmamd(\n",
        "    seed=47, sigmamd_range=[0.01, 0.02, 0.04, 0.08, 0.16])"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 44,
      "metadata": {
        "colab": {
          "height": 287
        },
        "executionInfo": {
          "elapsed": 274,
          "status": "ok",
          "timestamp": 1658264812303,
          "user": {
            "displayName": "",
            "userId": ""
          },
          "user_tz": 240
        },
        "id": "en247bQ515Kd",
        "outputId": "0aa5af2b-5656-4af2-9f09-44bd015df6b5"
      },
      "outputs": [
        {
          "data": {
            "image/png": "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\n8v/yoRAw+PczcwAAAABJRU5ErkJggg==\n",
            "text/plain": [
              "\u003cFigure size 600x400 with 1 Axes\u003e"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "font = {'family': 'normal', 'weight': 'normal', 'size': 14}\n",
        "matplotlib.rc('font', **font)\n",
        "\n",
        "for quant_name, metrics in vary_sigmamd_metrics.items():\n",
        "  distances = jnp.array(metrics['distances'])\n",
        "  plt.errorbar(\n",
        "      metrics['sigmamd'],\n",
        "      jnp.mean(distances, axis=1),\n",
        "      jnp.std(distances, axis=1),\n",
        "      label=quant_name)\n",
        "plt.legend()\n",
        "plt.xlabel('σ_md')\n",
        "plt.ylabel('RMSE')\n",
        "plt.xscale('log')\n",
        "plt.xticks(\n",
        "    ticks=metrics['sigmamd'], labels=[f'{s}' for s in metrics['sigmamd']])\n",
        "plt.grid()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "4ZtJn5ekzQzE"
      },
      "source": [
        "We then fix σmd = 0.1, k = 2 and vary the number of clients n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 45,
      "metadata": {
        "executionInfo": {
          "elapsed": 4032,
          "status": "ok",
          "timestamp": 1658264816488,
          "user": {
            "displayName": "",
            "userId": ""
          },
          "user_tz": 240
        },
        "id": "h6AewpRa3M9B",
        "outputId": "e9471305-e12a-4a5f-98b5-d406ed4d335b"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "average iteration duration = 0.006571912765502929 sec\n",
            "Finished Independent num_clients=10\n",
            "average iteration duration = 0.007915973663330078 sec\n",
            "Finished Correlated num_clients=10\n",
            "average iteration duration = 0.012097597122192383 sec\n",
            "Finished Independent num_clients=20\n",
            "average iteration duration = 0.014271211624145509 sec\n",
            "Finished Correlated num_clients=20\n",
            "average iteration duration = 0.02355680465698242 sec\n",
            "Finished Independent num_clients=40\n",
            "average iteration duration = 0.026767754554748537 sec\n",
            "Finished Correlated num_clients=40\n",
            "average iteration duration = 0.045855236053466794 sec\n",
            "Finished Independent num_clients=80\n",
            "average iteration duration = 0.05214083194732666 sec\n",
            "Finished Correlated num_clients=80\n",
            "average iteration duration = 0.09013292789459229 sec\n",
            "Finished Independent num_clients=160\n",
            "average iteration duration = 0.11289191246032715 sec\n",
            "Finished Correlated num_clients=160\n"
          ]
        }
      ],
      "source": [
        "def run_vary_num_clients(seed, num_clients_range):\n",
        "  rng, mean_rng, quant_rng = jax.random.split(jax.random.PRNGKey(seed), 3)\n",
        "  dimension = 1024\n",
        "  mean = jax.random.uniform(key=mean_rng, shape=[dimension])\n",
        "  sigmamd = 0.01\n",
        "\n",
        "  vary_num_clients_metrics = {}\n",
        "  for num_clients in num_clients_range:\n",
        "    quantizers = get_quantizers(\n",
        "        quant_rng,\n",
        "        num_levels=2,\n",
        "        num_clients=num_clients,\n",
        "        thresholds=jnp.array([0., 1. + 4 * sigmamd]),\n",
        "        update_thresholds=False)\n",
        "    # Just independent and correlated.\n",
        "    quantizers = only_independent_and_correlated(quantizers)\n",
        "    for quantizer_name, quantizer_agg in quantizers.items():\n",
        "      if quantizer_name not in vary_num_clients_metrics:\n",
        "        vary_num_clients_metrics[quantizer_name] = {\n",
        "            'num_clients': [],\n",
        "            'distances': [],\n",
        "        }\n",
        "      vary_num_clients_metrics[quantizer_name]['num_clients'].append(\n",
        "          num_clients)\n",
        "      distances = run_dist_mean(\n",
        "          quantizer_agg,\n",
        "          mean,\n",
        "          sigmamd,\n",
        "          num_clients,\n",
        "          rng,\n",
        "          dimension,\n",
        "          num_iters=10)\n",
        "      vary_num_clients_metrics[quantizer_name]['distances'].append(distances)\n",
        "      print(f'Finished {quantizer_name} num_clients={num_clients}')\n",
        "  return vary_num_clients_metrics\n",
        "\n",
        "\n",
        "vary_num_clients_metrics = run_vary_num_clients(\n",
        "    seed=47, num_clients_range=[10, 20, 40, 80, 160])"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 46,
      "metadata": {
        "colab": {
          "height": 287
        },
        "executionInfo": {
          "elapsed": 290,
          "status": "ok",
          "timestamp": 1658264816902,
          "user": {
            "displayName": "",
            "userId": ""
          },
          "user_tz": 240
        },
        "id": "FKxeknKM36qA",
        "outputId": "55eba98e-80d6-4aa8-a2e5-1ec8096bc847"
      },
      "outputs": [
        {
          "data": {
            "image/png": "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\nFSt48803rfsKCgo4efIkBw4coHbt2hdcY82aNYwaNYp169Zx+PBha9y7d++mbt26xcadmprKqVOn\n6NWr13nvzby8POvvkpaWRvPmzc+rTbVv75zTvEuiEE5LKUVMiB8xIX7c3SmaU3n5rNx52Jo4Xp2Z\nxqsz0wiv4UXXxiF0aRxCp4bBBPhIF9zKoFGjRiilSEtLu+jKcVrri34JKLz/7Mp0RRXdX1BQwEsv\nvcTNN998wbkhISEX7MvJyaFnz550796dL7/8ktDQULKysujcuXOJ7RoFBaaNbdq0aURGRp53zN3d\n3fq7VRaSKESl4eXuSudGpocUwP4jJ1m8xSSNmRv/ZuqqPbgoaFkv0DpSvFlEAK7SBdcpBQUF0bNn\nTz766CMeffTRC9opjhw5QlxcHPv27WPnzp3Wb+Lbt29n//79JCQklPo1W7duTXp6Og0bNrTp/PT0\ndLKysnjttdeIjo4G4H//+99555ztSVV47e6EhAQ8PT3ZtWsX3bp1K/baCQkJTJ48mZycHGtCW758\neal/p4ogiUJUWnUCvbmlXSS3tIvkTH4B6/ceYdFm0yj+/vwt/HveFgJ93OncKIQulm64oTW8HB22\nKGT8+PF06NCBxMRERo8eTfPmzdFas3DhQl5//XU2btxIixYtGDx4MB988AFaa4YOHUrr1q0v+gFc\nkhdffJG+fftSv359BgwYgJubGxs3bmTFihW89dZbF5wfGRmJp6cnH330EQ8//DBpaWm88MIL551T\nv359lFLMmDGDa6+9Fm9vb/z9/Rk2bBjDhg1Da02XLl3Izs5m+fLluLi4MGTIEG699VZGjhzJ3Xff\nzYsvvsj+/ft59dVXy1yW5UprXeW2Nm3a6LJauHBhmZ9bHTlreR3KPq3/b+1e/eTUdTpxzFxdf/h0\nXX/4dN3z34v0azNT9dKtB/XpvPwKj6u8yis1NdU+F/qit9kq0P79+/Ujjzyio6OjtYeHh65du7bu\n1auXnjlzpj527JjetWuXvv7667Wfn5/28/PT/fr103v27LE+/6WXXtJNmjS54LqA/v777y/Y/+uv\nv+pOnTppb29v7e/vr9u0aaM//PBD6/GuXbvqhx9+2Pp4ypQpukGDBtrT01O3bdtWz549WwPn/V++\n8sorOjw8XCul9B133KG11rqgoEB/8MEHOj4+Xnt4eOjg4GDdvXt3PWfOHOvzli9frlu1aqU9PDx0\n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\nhCjR/wOylTgXd8nSFwAAAABJRU5ErkJggg==\n",
            "text/plain": [
              "\u003cFigure size 600x400 with 1 Axes\u003e"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "for quant_name, metrics in vary_num_clients_metrics.items():\n",
        "  distances = jnp.array(metrics['distances'])\n",
        "  distances = jnp.log2(distances)\n",
        "  plt.errorbar(\n",
        "      metrics['num_clients'],\n",
        "      jnp.mean(distances, axis=1),\n",
        "      jnp.std(distances, axis=1),\n",
        "      label=quant_name)\n",
        "plt.legend()\n",
        "plt.xlabel('n')\n",
        "plt.ylabel('log_2(RMSE)')\n",
        "plt.xscale('log')\n",
        "plt.xticks(\n",
        "    ticks=metrics['num_clients'],\n",
        "    labels=[f'{s}' for s in metrics['num_clients']])\n",
        "plt.grid()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "OIXkn49QzS--"
      },
      "source": [
        "Finally, we fix σmd = 0.1, n = 100 and vary k (num_levels for quantization)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 47,
      "metadata": {
        "executionInfo": {
          "elapsed": 9324,
          "status": "ok",
          "timestamp": 1658264826369,
          "user": {
            "displayName": "",
            "userId": ""
          },
          "user_tz": 240
        },
        "id": "PwQGDp_g4j6x",
        "outputId": "bf53924b-f456-4b16-9816-9c849711c15e"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "average iteration duration = 0.0743243932723999 sec\n",
            "Finished Independent num_levels=2\n",
            "average iteration duration = 0.08959975242614746 sec\n",
            "Finished Correlated num_levels=2\n",
            "average iteration duration = 0.0805504322052002 sec\n",
            "Finished Independent num_levels=4\n",
            "average iteration duration = 0.18020777702331542 sec\n",
            "Finished Correlated num_levels=4\n",
            "average iteration duration = 0.07850604057312012 sec\n",
            "Finished Independent num_levels=8\n",
            "average iteration duration = 0.1702652931213379 sec\n",
            "Finished Correlated num_levels=8\n",
            "average iteration duration = 0.07107608318328858 sec\n",
            "Finished Independent num_levels=16\n",
            "average iteration duration = 0.16953704357147217 sec\n",
            "Finished Correlated num_levels=16\n"
          ]
        }
      ],
      "source": [
        "def run_vary_num_levels(seed, num_levels_range):\n",
        "  rng, mean_rng, quant_rng = jax.random.split(jax.random.PRNGKey(seed), 3)\n",
        "  dimension = 1024\n",
        "  mean = jax.random.uniform(key=mean_rng, shape=[dimension])\n",
        "  sigmamd = 0.01\n",
        "  num_clients = 100\n",
        "\n",
        "  vary_num_levels_metrics = {}\n",
        "  for num_levels in num_levels_range:\n",
        "    quantizers = get_quantizers(\n",
        "        quant_rng,\n",
        "        num_levels=num_levels,\n",
        "        num_clients=num_clients,\n",
        "        thresholds=jnp.array([0., 1. + 4 * sigmamd]),\n",
        "        update_thresholds=False)\n",
        "    # Just independent and correlated.\n",
        "    quantizers = only_independent_and_correlated(quantizers)\n",
        "    for quantizer_name, quantizer_agg in quantizers.items():\n",
        "      if quantizer_name not in vary_num_levels_metrics:\n",
        "        vary_num_levels_metrics[quantizer_name] = {\n",
        "            'num_levels': [],\n",
        "            'distances': [],\n",
        "        }\n",
        "      vary_num_levels_metrics[quantizer_name]['num_levels'].append(num_levels)\n",
        "      distances = run_dist_mean(\n",
        "          quantizer_agg,\n",
        "          mean,\n",
        "          sigmamd,\n",
        "          num_clients,\n",
        "          rng,\n",
        "          dimension,\n",
        "          num_iters=10)\n",
        "      vary_num_levels_metrics[quantizer_name]['distances'].append(distances)\n",
        "      print(f'Finished {quantizer_name} num_levels={num_levels}')\n",
        "  return vary_num_levels_metrics\n",
        "\n",
        "\n",
        "vary_num_levels_metrics = run_vary_num_levels(\n",
        "    seed=49, num_levels_range=[2, 4, 8, 16])"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 48,
      "metadata": {
        "colab": {
          "height": 290
        },
        "executionInfo": {
          "elapsed": 319,
          "status": "ok",
          "timestamp": 1658264826824,
          "user": {
            "displayName": "",
            "userId": ""
          },
          "user_tz": 240
        },
        "id": "n-HSAxzU4lVJ",
        "outputId": "fb2cf99d-ec06-483a-8734-f4970a3f8dbf"
      },
      "outputs": [
        {
          "data": {
            "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYoAAAERCAYAAABl3+CQAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90\nbGliIHZlcnNpb24zLjMuNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8QVMy6AAAACXBIWXMAAAsT\nAAALEwEAmpwYAABGLUlEQVR4nO3dd3hU1dbA4d9KJ4USUiAJkA4JHUKHkFBEml1UbIBSRBRU1Ou1\ncu3KxQoIFkAQUdHLJ0166EhHSigJHZQigtJL9vfHGZIQQkifSVjv8+yHzJlTFtmTWafsIsYYlFJK\nqWtxsncASimlHJsmCqWUUjnSRKGUUipHmiiUUkrlSBOFUkqpHLnYO4Ci4OfnZ0JDQ/O17alTp/Dy\n8ircgFSBab04Hq0Tx1SQelmzZs1RY4x/1uWlMlGEhoayevXqfG2blJREQkJC4QakCkzrxfFonTim\ngtSLiOzJbrneelJKKZUjTRRKKaVypIlCKaVUjjRRKKWUypEmCqWUUjkqla2elLrRpKWlsX//fk6d\nOlVsxyxXrhzJycnFdjyVO9eqF1dXVwICAihbtmye96mJQqlS4OjRo4gI1atXx8mpeG4U/PPPP/j4\n+BTLsVTuZVcvxhjOnDnDgQMHAPKcLPTWk1KlwPHjxwkMDCy2JKFKFhHB09OT4OBgDh8+nOft9Yoi\nk26fLeP48dNoHyJV0ly6dAlXV1d7h6EcXJkyZbhw4UKet9PTj0yO/HOOAycNh/8+a+9QlMozEbF3\nCMrB5fczookii9MXocOHi5i58Xd7h6JUsbtn1HLuGbXc3mEoB6OJIpOAsh5UKyuEVPDksW/W8vT3\n6/n7bN4v05RSqjTRRJHJd32b8VpzT37q35wn20bxf+sP0vHDxSxP/dPeoSmlrqFLly706NHD3mHk\nWq1atXjttdfsHUaeaKLIhquzE0+3j+aHfs1wdRa6f7GCN6Zt4eyFS/YOTalSpUePHnTp0sXeYZR4\nr732GrVq1Sqy/WuiyEGDqhWYMbAV9zepyhdLdnHLp0vYfPCEvcNSSqlipYniOjzdXHjjttqM6dmI\nv05f4LbhSxmRlMKlNGPv0JQqVS5fXXz00UcEBwdToUIFevbsyenTp9PXOX36ND169MDb25vAwEDe\neuutq/Zz/vx5nn/+eUJCQvDy8qJRo0bMmjUr/f2kpCREhGnTplGvXj08PDxo2LAha9asuWI/y5Yt\no3Xr1un9Dx577DH+/vvv9PcTEhLo378///73v/Hz8yMgIIDBgweTlpaWvs7hw4e59dZbKVOmDNWq\nVeOrr766Kt4TJ07Qp08fAgIC8PHxoXXr1lfMpzN27Fi8vb2ZN28etWrVwsvLi8TERHbt2pX+/pAh\nQ9i8eTMiQtmyZRk7dmzeKyAH2o8ilxKrBzB7UDwvTtnIe79sY37yYYZ1q0fVip72Dk2pqwyZupkt\nB/++/opZbPnd2iY3LZ8uXbqEs7Nz+uvYoLK82rVmno+Z2eLFi6lcuTJz585l3759dOvWjejoaF54\n4QUABg8ezJw5c/jxxx8JDg5myJAhLFq0iDvuuCN9Hz179iQ1NZWJEycSEhLCjBkz6Nq1K6tWraJu\n3brp6w0ePDg9KQ0ZMoTOnTuzc+dOPD092bhxIzfddBNDhgzhiy++4NixYwwaNIhevXoxefLk9H18\n8803DBw4kGXLlrF+/Xq6d+9Ow4YNue+++wAr+e3Zs4e5c+fi6enJU089xe7du9O3N8bQuXNnypUr\nx7Rp0/D19WXcuHG0adOGbdu2UblyZQDOnTvH22+/zVdffYWHhwcPP/ww/fr1Y9asWdxzzz1s2rSJ\nadOmkZSUxMmTJwkODi5QPWSlVxR5UMHLjeHdG/DBPXXZ9sc/dPxoEZNW7sUYvbpQqjCULVuWkSNH\nEhMTw0033cTdd9/NvHnzADh58iRffvkl7733Hh06dKBWrVqMGTPmit7oqampfPvtt3z//ffEx8cT\nHh7OgAED6NSpE6NGjbriWC+//PIV+zl79iwTJ04E4P333+eee+7hmWeeISoqiiZNmjBy5Eh+/PHH\nK3o2x8bG8p///Ifo6Gi6detGYmJierzbt29n5syZjB49mhYtWlC/fn3GjRvHmTNn0rdfsGAB69ev\nZ/LkyTRu3JjIyEhef/11wsPDGT9+fPp6Fy9eZPjw4TRu3Jg6deowePBgFixYQFpaGmXKlMHb2xsX\nFxcqVapEYGAgZcqUKdR60SuKPBIRbq8fQuOwigz+fgP/+mkjc5MP8fYddfD3cbd3eEoB5PvM/vKV\nxHd9m1133aIY6yk2NhYXl4yvpaCgIH799VfASgLnz5+nWbOM2Ly9valdu3b667Vr12KMITY29or9\nnjt3jjZt2lyxLLv9bNmyBYA1a9aQkpLCd999l77O5RPC1NRUAgICAKhTp84V+wwKCkpPJMnJyTg5\nOdG4ceP096tVq0ZQUFD66zVr1nD69Gn8/a+cpvrs2bOkpqamv3Z3d6d69epXHOfChQscP34cX19f\nipominwKLl+Gbx5twldLd/HerG3c/OEi3rqjNh1qVrJ3aEqVWFmHIRGR9Hv+ublyT0tLQ0RYtWrV\nVfvKy1l2Wloajz76KE899dRV72W+rVMY8QYGBrJ48eKr3ss8cF/m5Hn5OJe3Lw6aKArAyUl4tFU4\n8dH+DJq0nr7j19AtLoSXu8Ti46Hj7ihVmCIjI3F1dWXFihWEh4cDcOrUKTZt2kRERAQA9evXxxjD\nH3/8QWJiYo77y24/Dz30EAANGjRg8+bNREZG5jvemJgY0tLSWLVqFc2bNwdg7969HDx4MH2dBg0a\ncOjQIZycnNJjyQ83NzcuXSq65vv6jKIQRAf6MOXxFjyeGMHkNfvp+NFiVu46Zu+wlCpVvL29eeSR\nR3j++eeZM2cOmzdvplevXld8QUZHR3P//ffTo0cPJk+ezM6dO1m9ejVDhw7lp59+umJ/b7zxxhX7\ncXNzo3v37gA8//zzrFy5kn79+rFu3TpSUlKYNm0affv2zXW81atX5+abb6Zv374sX76c9evX06NH\njyuubNq1a0eLFi249dZbmTlzJrt27WL58uW8+uqr2V5lXEtoaCh79uxh7dq1/Pnnn5w7dy7X2+aG\nJopC4ubixLMdavBDv2Y4iXDP6OW8PTOZcxe1k55ShWXo0KEkJiZy++23k5iYSK1atYiPj79inTFj\nxtCzZ0+ee+45atSoQZcuXVi0aBHVqlW7Yr133nmHZ555hgYNGrBjxw6mTZuGl5cXYD17WLRoEbt3\n76Z169bUrVuXF154gcDAwDzFO3bsWMLCwmjTpg1du3ale/fuhIaGpr8vIsyYMYM2bdrQu3dvqlev\nTrdu3di2bdsVzzKu584776RTp060bduWsLAwvv322zzFeT1SGlvsxMXFmcztkPMiKSmJhAKOM37q\n3EXemL6Fb1fuo0YlHz68tx41KuV9VimVoTDqpTRLTk4mJiamwPux98Ps4pCUlERiYiJHjhzBz8/P\n3uEUuuvVS06fFRFZY4yJy7pcryiKgJe7C2/fUYcvH47j6Mlz3PLJUkYvStVOesrhfde3Wa6ShLqx\nOHyiEJH+IrJLRM6KyBoRaWXvmHKrbUwgswbFk1Ddn7dmbOW+z1ew79jp62+olFIOxKEThYjcA3wE\nvAXUB5YBM0Wkql0Dy4OK3u6MerAh799Vhy0H/6bjR4v5YfU+7aSnlJ0kJCRgjCmVt52KikMnCuBp\nYKwx5nNjTLIx5gngd+AxO8eVJyLC3XFVmDmwFbFBZXl28m/0m7CGP08WbssEpZQqCg6bKETEDWgI\nzM7y1mygefFHVHBVfD35tndT/t2pBgu2HqHDh4uZl3zI3mEppVSOHLbVk4gEAQeA1saYRZmWvwLc\nb4ypnmX9PkAfgMDAwIaTJk3K13FPnjyJt7d3vuPOrX3/pDH6t3Ps+yeNhBAX7q3hhoeLznl8LcVV\nLyVVuXLlCtQ5LD+yDgqoHMP16iUlJYUTJ7KfLiExMTHbVk8loWd21kwm2SzDGDMaGA1W89j8NqUs\nzmaY3TpeYtic7YxetJNdZ9wY1q0ODasV/bgtJZE2j81ZcnJysTdVLanNY0u769WLh4cH9evXz9M+\nHfbWE3AUuARkHTwpACgV92vcXZx5oWMMk3o35VKa4e7PlvP+rK2cv1g847codZUxna2iVCYOmyiM\nMeeBNUD7LG+1x2r9VGo0Ca/IzIGtuLNBCMMXpHL7iKVsP/SPvcNSSinAgROFzTCgh4g8KiIxIvIR\nEAR8Zue4Cp2Phyvv312XUQ825PcTZ+nyyRK+XLKLNO2kp1SxSkhIYMCAAXY59tGjRxERkpKS7HL8\na3HoRGGM+Q4YBLwErAdaAp2MMXvsGFaR6lCzErMGxRMf5cfr07bwwJe/cuD4metvqFQJdejQIQYO\nHEhERATu7u4EBwfTsWNHZsyYYe/QcuXy1KpHjx61dyhFxqETBYAxZoQxJtQY426MaZi5BVRp5e/j\nzucPxfHOHbVZv+84N3+4iP+t26+d9FSps3v3bho0aMCsWbN4++23+e2335g7dy6dO3emX79++drn\nxYsXs/1bOX/+fEHDvWE5fKK4UYkI9zauysyBrYgO9OGp7zYwYOI6/jqlH3ZVevTv3x9jDKtXr6Zb\nt25Ur16dmJgYBgwYwIYNGwBrDofbb78dHx8ffHx8uOOOO9i/f3/6Pl577TVq1arF2LFj069KTp06\nhYgwfPhw7rjjDry8vPj3v/8NwNSpU2nYsCEeHh6EhYXx4osv5phEJkyYQKNGjfDx8SEgIIC7776b\nAwcOAFaiuzzvhb+/PyJCjx49AGviovfee4+IiAjKlClD7dq1mTBhwhX7XrVqVXos9evXT5/Nz9GU\nhOaxN7RqFb34vm8zRi1K5YM521m1+xjv3VWHhOoB9g5NObKZ/4I/NuZ9uz9+s/7NRcunMpcugnOm\nr5BKtaHjO7k+1LFjx/jll1944403su0jU6FCBYwx3HbbbXh4eDB//nxEhAEDBnDbbbexatWq9Jne\ndu3axcSJE/nhhx9wc3PDw8MDgCFDhvDWW28xdOhQRIRZs2Zx//3389FHHxEfH8/evXvp168f586d\nY+jQodnGef78eYYMGUKNGjU4evQozz//PPfddx+LFi2iSpUq/Pjjj9x5551s3rwZX1/f9PkmXnrp\nJSZPnszw4cOpXr06y5cvp3fv3lSoUIHOnTtz6tQpOnfuTOvWrRk3bhwHDhxg0KBBuf79FSdNFCWA\ns5PQPyGS+Ch/nvpuPT3GrOKBplX5d6cYPN20ClXJlJKSgjEmx+HR586dy4YNG0hNTU2fx2HixIlE\nRkYyb9482rVrB1hf5uPHj79qvoh77rmHRx99NP31ww8/zLPPPkvPnj0BiIiI4N133+WBBx7g/fff\nT088mfXq1Sv95/DwcEaOHElMTAz79+8nJCQkfc7qgICA9PGjTp06xbBhw5g9ezatWlnjmIaFhbFy\n5UqGDx9O586d+eabbzh//jxjxozB29ubWrVq8eKLL/Lggw/m9VdZ5PRbpgSpFVyOqU+0ZOisbXy5\ndBdLU/5kWLe61K9awd6hKUeThzP7K1y+kug5/bqrnilgh7vcPHNLTk4mKCjoisl+wsPDCQoKYsuW\nLemJIiQkJNtJheLiruxkvGbNGlauXMm7776bviwtLY0zZ87wxx9/ULly5av2sXbtWoYMGcL69es5\nduxYetx79+4lJCQk27i3bNnC2bNnufnmm69IPhcuXEj/vyQnJ1OnTp0rrqaaNXPMId41UZQwHq7O\nvNQlljYxAQz+fgN3fbacxxMjeaJNJK7O+shJlRxRUVGICMnJydx+++3ZrmOMyfYsH7hi+eWZ6bLK\nujwtLY1XX32Vu++++6p1/f39r1p26tQpOnToQLt27Rg/fjwBAQEcPXqUVq1a5fhcIy3N6jQ7depU\nqla9crBrV1fX9P9bSaHfLCVU8wg/fnkqnlvrBfHxvB3cOXIZKYdP2jsspXLN19eXDh068Omnn3Ly\n5NWf3ePHjxMbG8uBAwfYvXt3+vKdO3dy8OBBYmNj83zMBg0asHXrViIjI68qLi5Xnzdv3bqVo0eP\n8tZbbxEfH0+NGjU4fPjwFeu4ubkBXDF3d2xsLO7u7uzZs+eq41yekjU2NpaNGzdy6tSp9O1WrFiR\n5/9TcdBEUYKV9XBlWLd6jLi/AXuPnabzx4sZt2y3dtJTJcaIESMwxhAXF8cPP/zAtm3b2Lp1KyNH\njqROnTq0a9eOunXrcv/997NmzRpWr17N/fffT4MGDWjTpk2ej/fKK68wceJEXnnlFTZt2sTWrVuZ\nPHkyzz33XLbrV61aFXd3dz799FN27tzJ9OnTefnll69Yp1q1aogI06dP58iRI5w8eRIfHx8GDx7M\n4MGD+eqrr0hJSWH9+vV89tlnjB49GoDu3bvj4uJCr1692Lx5M3PmzOHNN9/M+y+xGGiiKAU61a7M\n7EHxNIuoyKs/b+bhMSv548RZe4el1HWFhYWxdu1a2rdvz/PPP0+dOnVo06YNP//8M6NGjUJEmDJl\nCv7+/iQkJJCYmEilSpWYMmXKNW9J5aRDhw5Mnz6dBQsW0LhxYxo3bsw777xz1e2hy/z9/Rk3bhxT\npkwhNjaWIUOGMGzYsCvWCQ4OZsiQIbz44osEBgam9+p+/fXXee211xg6dCg1a9akffv2/Pjjj4SF\nhQHg7e3NtGnT2LFjBw0aNGDw4MFXPDtxJA47zHhBxMXFmdWrV+dr25I8Sqkxhm9+3cub05Nxc3Hi\n9dtqcUvdIHuHVShKcr0Uh+Tk5BxbD+VaHh5m6+ixjul69ZLTZ0VEsh1mXK8oShER4YGm1ZgxsBVh\nfl48+e06nvh2HSdOX7B3aKqk6Dk9V0lC3Vg0UZRCYX5eTO7XjGfaRzNz4+90+HARi3ccsXdYSqkS\nShNFKeXi7MQTbaP4X/8WeLk78+CXK3nt582cOX/p+hsrpVQmmihKudoh5Zj+ZCt6NA9l7LLddPlk\nMb/tP27vsJRSJYgmihuAh6szr91SkwmPNOHUuUvcMWIZH8/bwcVLOpNeaVIaG6aowpXfz4gmihtI\nyyg/Zg2Kp1Ptygybs527PlvOziPaSa80cHZ25sIFbbSgcnbmzJn0nuF5oYniBlPO05WP76vPJ/fV\nZ9fRU3T+eAnjV+zRs9ESrnz58hw6dCh96AilMjPGcPr0aQ4cOEBAQN5Hntaxnm5QXesG0SjUl2cn\nb+DlKZuYl3yI9+6sQ0BZD3uHpvLBz8+P/fv3s23btmI75tmzZ9OH81aO41r14urqSmBgIGXLls3z\nPjVR3MAqlfPg616NGb9iD2/NSOamDxfx1u216VT76hE0lWNzcnK6Zu/iopKUlET9+vWL9Zjq+oqi\nXvTW0w1ORHioWSjTn2xFNV9P+n+zlqe/W8/fZ/V+t1LKoolCARDh783kx5ozsG0U/7fhIDd/sIhl\nqaV3snilVO5polDpXJ2deKp9ND8+1hx3V2e6f/4rr0/bwtkL2klPqRuZJgp1lXpVyjP9yZY82LQa\nXy7ZxS2fLmHTgRP2DkspZSeaKFS2PN1ceP22Wozr1Zjjpy9w+4ilDF+QwiWd60KpG44mCpWj1tH+\nzBoUz02xlXh/1ja6jVrOnj9PXX9DpVSpoYlCXVcFLzc+7V6fD++px/ZD/9Dxo8V8u3KvdtJT6gah\niULliohwW/1gZg2Kp16V8rzw00YeHbeaI/+cs3doSqkipolC5UlQ+TJMeKQJr3SJZXHKUTp8uIhZ\nm/+wd1hKqSKkiULlmZOT0KtlGNOfaElQeQ/6jl/D4B828I920lOqVNJEofItKtCHnx5rwYDESH5a\nu5+bP1zMrzv/tHdYSqlCpolCFYibixODO1Tnh37NcXEW7v18BW/PSObcRe2kp1RpoYlCFYqG1Sow\n48lW3Ne4KqMW7eTWT5eS/Pvf9g5LKVUINFGoQuPl7sJbt9fmqx5xHD15nls/XcqohanaSU+pEi5P\niUJEPEQkUUR6ikh/EblbRCKKKjhVMrWpEcisQa1IrOHP2zO3ct/oFew7dtreYSml8ilXiUJEWojI\n98BfwBxgKPBvYBywXUR2iMizIuJTWIGJSB8RWSAix0XEiEhoYe1bFb2K3u589kBDht5dly2//03H\njxbzw+p92klPqRLouolCRP4P+B7YA9wE+BhjKhpjQowxnkAU8AbQFitptC+k2DyB2cBrhbQ/VcxE\nhLsahvDLoFbUDCrLs5N/o+/4Nfx5UjvpKVWS5OaKYjYQZox51hiz2BhzJvObxpidxphxxpibgXaF\nFZgx5kNjzNvAksLap7KPkAqefNu7KS92iiFp2xE6fLiIuVsO2TsspVQuiaPfChCROGAVVrLancN6\nfYA+AIGBgQ0nTZqUr+OdPHkSb2/vfG2rrm/fP2mM/u0c+/5JIz7EhftquFHGRa67ndaL49E6cUwF\nqZfExMQ1xpi4rMtzlShE5CZgvjHmou21jzHmn0zvewDdjTFf5Su6nI+dq0SRWVxcnFm9enW+jpeU\nlERCQkK+tlW5c+7iJT6Ys4NRi1KpUsGTYd3qEhfqm+M2Wi+OR+vEMRWkXkQk20SR21ZPM4HMf8kH\nRCQ80+tywOe5COIN24PpnEpCLmNSJZS7izP/6liD7/o0I80Yuo1aznu/bOX8xTR7h6aUyoZLLtfL\nem/g+vcKsvchMOE66+zN575VCdM4zJdfBsXz+tQtjEhKJWnbET68tx7RgYXWeE4pVQhymygKhTHm\nKHC0OI+pHJu3uwvv3lWHdrGB/OvH3+jyyRKe61CdXi3CcHLK7/mIUqowOWzPbBGpJCL1gGjbolgR\nqSciOd/MViVS+9hAZj0VT3yUP29MT+b+L37lwPEz199QKVXk8nJFUUdEjtl+FqCmiJS3vfYr1Kgs\n/YBXM72ebvu3JzC2CI6n7MzP253PH2rID6v3M2TqZm7+YBFDbq3JpJV7OXHiDPrcVCn7yEuimMWV\nzyb+L8v7hdrO1hjzGtrZ7oYjInRrVIWm4RV55of1PP39Bip4uuLr6tjNuJUqzXJ76ykMCLf9e60S\nfs2tlcqjqhU9mdSnGc/fXIPjpy+w84ThjWlbOKq9upUqdrm6ojDG7CnqQJTKytlJeCwhgl82/c6u\nwyf4aukuJq7cy8PNQ+nTKpwKXm72DlGpG0JuBwX0FpGKWZbFiMhXIvK9iNxXNOEpBf83oCUft/Fi\nztOtaRcTyGcLU2n13gKGzdnOiTM6/apSRS23t55GAkMuvxARP2Ax0AWoDkwQke6FH55SGSL8vfn4\nvvr8MjCeVlF+fDxvB63enc+n83dw8txFe4enVKmV20TRDJiS6fWDwHkgyhhTF2vY8QGFG5pS2ate\nyYeRDzRk2hMtaRzmy9DZ22n17nxGLUzlzHmdglWpwpbbRFEZSMn0OhH40RhzwvZ6HNZw40oVm1rB\n5fji4UZMebwFtUPK8/bMrbR6bwFfLdnF2QuaMJQqLLlNFKcBr0yvGwMrMr0+izV/hFLFrl6V8nzd\nqzGT+zUjKsCb/0zbQsL7SYxfsUfHj1KqEOQ2UWzA6uiGbdA+f2B+pvcjgIOFGZhSeRUX6su3fZoy\nsXcTQiqU4eUpm0gcmsR3q/Zy4ZImDKXyK7eJ4nWgv4jsxRpJdqwx5vdM79+OTjCkHETzCD9+6NeM\ncb0a4+ftxvM/bqTdsIX8tHY/l9K0455SeZXbfhQLRaQh1lSofwA/ZFllPbCycENTKv9EhNbR/sRH\n+TEv+TDD5mzn6e83MHxBCoPaRdO5dmUddFCpXMr1EB7GmGQg+RrvjS60iJQqRCJCu9hA2tQIYNbm\nP/hg7nae+HZdesLoUDMQEU0YSuUkV4lCRBrkZj1jzNqChaNU0XByEjrWrsxNNSsx7beDfDR3B/0m\nrKFWcFmebh9NYvUATRhKXUNuryhWkzHo37X+mgzgXOCIlCpCzk7CrfWC6Vy7MlPWH+SjedvpNXY1\n9aqU55mbomkZ6acJQ6kscpsozmM9mxgDfI/VXFapEsvF2Ym7GoZwa70gJq/ZzyfzdvDglytpHOrL\n0zdF0zS84vV3otQNIretnioB7wO3YvWfeBEINMbsyVyKKkilioqrsxP3Na7KgmcT+M+tNdn95ynu\nHb2C+79YwZo9f9k7PKUcQq4ShTHmuDFmuDGmAZCAdYUxU0Q2i8hTIuKwM+UplRvuLs481CyURc8l\n8lLnGLb98Q93jlxGjzEr+W3/cXuHp5Rd5fkL3hizzhgzAIgFDmGN81S+kONSyi48XJ15tFU4i55L\n5Pmba7B+33Fu+XQpvb9eTfLvf9s7PKXsIs+JQkQSRWQ8kAq4Ao8Aeo2uShVPNxceS4hg8XOJPN0+\nmhU7/6TjR4t5/Ju17Dj0j73DU6pY5bZ5bAjWEB49AA9gPNDAGLO96EJTyv58PFx5sm0UDzcL5Ysl\nO/lqyS5mbPqdW+sGMbBdNGF+XtffiVIlXG5bPe0CDgBjgRnARcA7a/8K7UehSqtynq48c1N1erYI\nY9SiVMYt283U337njvrBPNk2iiq+OiamKr1ymyicgarAK8DLtmVZG5trPwpV6vl6ufFCxxgebRnO\nyKRUJvy6h/+tO0C3RlUYkBhJUPky9g5RqUKX20QRVqRRKFXC+Pu480rXWPrEhzN8QQqTVu1l8ur9\ndG9Slf4JEQSU9bB3iEoVmtwOCqh9JJTKRqVyHrx+Wy36tg7n0/kpjF+xh29X7uWhZtXo1zqCit7u\n9g5RqQIrlP4PInKXiGwqjH0pVRKFVPDknTvrMP+Z1nSuU5kvl+yi1XsLeO+XrRw/fd7e4SlVILlO\nFCLSW0R+EJGJItLEtqy1iKwDvgaWFlWQSpUU1Sp6MaxbPWY/1Zq2MYGMXJhKq3cX8MGc7fx99oK9\nw1MqX3KVKERkMDAc61nFrcB8EXkOmAxMAaoaY/oWVZBKlTSRAd58cl99Zg5sRfPIinw0bwet3l3A\n8AUpnDp30d7hKZUnub2ieAToZ4yJAzoDZbAmMYoyxgwxxhwtqgCVKslqVCrLqAfjmPZES+KqVeD9\nWdto9d4CRi9K5cz5S/YOT6lcyW2iqAbMBTDGJAEXgBeNMceLJiylSpdaweX4skcj/te/OTWDyvLW\njK3Ev7+AMUt3cfaCJgzl2HKbKDyAs5lenweOFH44SpVu9atWYPwjTfi+bzPC/bwYMnULiUOTmLBi\nD+cvptk7PKWyleupUIF+InIy03aPiMifmVcwxgwrtMiUKsUah/kyqU9TlqX+yX9nb+OlKZv4bGEq\nT7aJ4o4Gwbg464DMynHkNlHsxRrr6bI/gO5Z1jGAJgqlcklEaBHpR/OIiizcfoRhc7bz3I+/MSIp\nhYHtorilbjDOTjrbnrK/3Ha4Cy3iOBzD8GY0+fsoRH8PleqCk57VqaInIiRUD6B1tD9zkw8zbM52\nnvpuA8MXpDKoXRSdalXGSROGsiOH/CYUEV8R+UREtorIGRHZJyIjRaRo56c8e5wy5w7D6AQYGgU/\n9oYN38FJfRyjip6I0D42kOlPtGTE/dZ4mwMmrqPTx4uZtfkPjDHX2YNSReO6VxQi8gDwjcnFp1RE\nQoEqxpjFBYwrCAgGngO22H4eAXyL1Sy3aDyTzNLZU2gReA5S5kLqPNj4vfVe5boQ2Q4i2kKVxuDs\nWmRhqBubk5PQqXZlOtSsxNQNB/lo3g76jl9D7eByPN0+moTq/ojoFYYqPrm59dQLeEVExgI/A5sz\nJw0R8QVaAg9gTZPaq6BBGWM2AXdkWpQiIs8C00SkrDGmyKYau+BWHuomQN17IC0N/thgJY2U+bDk\nQ1j8X3DzgfDWENnWShwVqhVVOOoG5uwk3FY/mC51KvO/dQf4eP4Oeo5dRf2q5XmmfXVaRFbUhKGK\nxXUThTGmjYh0Bp4EXgfOishhrOayFQB/4DAwBnjcGFNU92nKAueA00W0/6s5OUFQfavEPwtnT8DO\nhdaVRso82DrNWq9ilHW1EdkWqrUAN52bQBUeF2cn7o6rwq31gpm8Zj+fzN/BA1/+SuMwX55pH02T\n8KK9I6uU5OW+p4j4YV09VMPqnX0UWAesM8YUWSNwESkPrAJmGmOevMY6fYA+AIGBgQ0nTZqUr2Od\nPHkSb2/v669oDJ6n9+N7bB2+x9ZS7sRmnNPOkyauHC9fk2O+DTjmW5/TnlVAz/oKLNf1cgM4f8mw\ncP9Fpu28wIlzhpoVnbg9yo3I8sU7HYzWiWMqSL0kJiausY3AcYU8JYqCEpE3gBevs1qirff35W28\ngF+AS8DNxpiz19rwsri4OLN69ep8xZiUlERCQkLeN7xwBvYsta40UubB0W3W8rIhENnGuuIIaw1l\nyucrrhtdvuulFDtz/hITVuxh5MJUjp06T2J1f55uX53aIeWK5fhaJ46pIPUiItkmirx0uCsMHwIT\nrrPO3ss/iIg31tSrAF1ykyTsxrWM7fZTO+v18X22W1RzYfMUWPs1iDOENLKt1wYq19cmuCrfyrg5\n0zs+nO5NqjJ22W5GL9pJ10+XcFNsIE+1jyamcll7h6hKiVwnChG5F7gT+AsYZYxZk+k9P2ClMSY8\np33YBg/M1QCCIuIDzMSacvVmY8zJ62ziWMpXgYY9rHLpAuxfndGSasEbVvGsCBFtrAfiEW3AJ9De\nUasSyMvdhccTI3mwWTW+WrKLLxfvYvaWxXSuU5mn2kURGeBj7xBVCZerRCEiPYHRwE9AFWCZiDxh\njBltW8UZ67lFobAlidlYD7BvA7xst6AAjhljStZMMM6uUK2ZVdq+bPXL2LnAukWVOg82/mCtV6l2\nxlVJSGNwcbNv3KpEKevhyqB20fRoHsrni3cyZuluZm78nVvrBTOwbRShfl7X34lS2cjtFcUg4Alj\nzGcAItIF+EZE3I0xnxRBXA2Bpraft2d5LxFIKoJjFh9vf6jTzSppaXBoo60J7jxY9gks+QDcvK1n\nGpFtrVIh1N5RqxKivKcbz3aoQa8WYYxatJOvl+/m5w0HubNBME+0iaKKr7bKU3mT20QRifVAGQBj\nzDQR6QTMEBFnrI5whcb2MPvGaCrk5GR15qtcF1o9A2f/hl2LMp5vbJturVcx0rpFFdkOQluAm54d\nqpxV9Hbn351ieLRVGCMWpDLx1738b90BusVVYUCbSCqXK2PvEFUJkdtEcQKoDOy+vMAYs9TWv2KG\n7T1VGDzKQkwXqxgDf6bYWlLNtR6IrxwFzm5QrXlGT/GAGG2Cq64pwMeD126pSd/W4Xw6P4XvV+/j\nhzX76d64Kv0TIwjw8bB3iMrB5TZRrAQ6AsszLzTGLLHdhpqR7VaqYETAL8oqTfvBhbOwd1lGE9zZ\nLwEvgU9QRhPc8AQoU8HekSsHVLlcGd68vTb9WkfwyfwdjF+xh0mr9vJQs1D6xodT0dvd3iEqB5Xb\nRPEB0Dy7N4wxi2zJ4uFCi0plz9XD1kqqDXR4E07sz3ggvmUqrJsA4gTBcRkPxYPqgVPxdsRSjq2K\nryfv3VWX/gmRfDRvB58v3smEFXvo2SKU3q3CKe+pjSjUlXI7zPhCYGEO7yeR6QGziPwL+EynSi1i\n5UKg4cNWuXQRDqyxPRSfC0lvQ9Jb1tXF5Sa4kW3Bp5K9o1YOItTPiw/uqcfjiRF8MHcHwxek8vWy\nPTzSKoxeLcMo66EDXypLUXW4+zfwPXC8iPavsnJ2gapNrNLmRTj1Z0YT3JS5sOlHa73A2hm3qao0\n1Sa4isgAH4Z3b8CAxL/5YM52Ppy7gzFLd9MnPpwezUPxci/ufrnK0RTVJ0CfrNqbV0WofZdV0tLg\n0KaMwQyXj4ClH4GrF4TFZzTB9c2xv6Qq5WIql2X0Q3Fs3H+CYXO28f6sbXy1ZBf9WkfwQNNqlHHT\nW5g3Kj1VuBE4OUHlOlZp+RSc+wd2Lc64TbV9prWeb3hGS6rQluCuA77diGqHlGNMz8as2fMXH8zZ\nzpszkhm9eCePJ0RwX5OquLtowrjRaKK4Ebn7QI1OVjEGju3M6PC3bgKsHG01wa3aNOOheECsNsG9\nwTSsVoEJjzbh151/8t8523lt6hZGLdrJgDaR3N2wir3DU8VIE8WNTgQqRlilSV+4eA72Ls+YrGnO\nK1bxqWx7IN4GwhPB09fekati0iS8It/1acrSlD/575xtvPi/Tfxn6hbKuxkWt0zDzUUHtiztNFGo\nK7m4W30xwhOsSWf/PpjRBHfrNFh/uQluw4ye4sENtAluKScitIzyo0VkRZK2HWHAxLUcOp1G6/cX\n0Cc+nHsbVdVnGKVYUSWKxcCZItq3Kk5lg6DBg1ZJuwQH1mY821j4Lix8BzzKQ0RixvONstpRv7QS\nERJrBFAzqCwHjh4nuIInQ6Zu4ZP5KfRsHspDzUIp56nNakubPCUKEal6jbcMcPbyNKjGmE4FDUw5\nICdnqNLIKokvwOljmZrgzoPN/7PWC6iZ0ZKqajPrKkWVKt/3a26bIKcZq3YfY8SCFP47ZzujFu3k\n/qZVeaRlmA4NUork9YpiN1ZSyJaI/I01d/ZzxpiLBYhLlQSevlDrTqsYA4c2ZwxmuGIkLPsYXD2t\nJriXO/xVjLB31KqQNQr1ZUzPxmw+eIKRSal8vsga4rxbXAh94yN0tNpSIK+J4j7gPeAz4FfbsiZY\nc1W/BpQHXgL+AV4tlAhVySAClWpZpcVAOHcSdi/JmKxpu23w4QqhGS2pQltpE9xSpGZQOT7t3oBd\nR08xamEq363ax7cr93FL3SAeS4ggOlAnUCqp8pooHgOeMsb8lGnZfBHZBgw0xrQWkcPAEDRR3Njc\nvaH6zVYB+DMVUudbt6jWfwurvgAnV1sTXNtD8cBa2gS3FAjz8+KdO+swsF0UXyzelT68efvYQPon\nRFC/qg5aWdLkNVE0ATZms3wT0Mj283IgpCBBqVLochPcxr2tJrj7fs3ouzH3Nat4B2bcoopoo01w\nS7jK5crwcpdYHk+MZOyy3Yxbtps5Ww7RPKIi/RMiaRFZEdETgxIhr4liD9ZtpmezLO8N7LX97A8c\nK2BcqjRzcbeeW4TFQ/v/wN+/2642bL3EN0wExGp2e7kllblk76hVPvl6ufF0+2j6xIcz8dc9fLF4\nFw98+St1Q8rRPzGS9jGBODlpwnBkeU0UzwA/2ma3W4X1YLsREAHcaVunEdaAgErlTtnKUP9+q6Rd\ngoPrMgYzXPQ+LHyXeHGGv7tDTFdrilhXbVFT0ni7u9AnPoKHmoXy49r9jFq4k77j1xAV4E2/1hHc\nUi8IV2ftvOeI8pQojDHTRSQK6A9Uxxr872esIcX32tYZUehRqhuHkzOExFkl4fn0JrhHFo0hcPMU\nWDfemk88sp2VNKLag0c5e0et8sDD1Zn7m1TjnrgqTN/4OyMWpPLMDxsYNmc7fVuH0y2uCh6u2nnP\nkeS5w50xZh/wQhHEotTVbE1wk49WJLBlM2sww61TYesM2DLFeiAeFm9NHVu9k863UYK4ODtxa71g\nutYJYv7Ww4xISuGV/9vMx/N20KtlGA80raZzYjiIPCcKEQkEHgdisW49bQZGGmMOFXJsSl3JxR2i\n2lml8wewf5U1rMjWaTDtKZj2NIQ0spJGjS7aZ6OEcHIS2sUG0jYmgF93HWP4ghTe+2UbI5NSeahZ\nNXq2CMNPp2m1q7z2zG4B/AIcImP+7AeAp0WkgzFm+TU3VqowOTllTNTU/j9wOBm2TreuNi4PZOgf\nY0sanaFyPW166+BEhKbhFWkaXpGN+08wIimFEUmpfLlkF/c2qkrv+HCCy5exd5g3pLxeUQwFvgX6\nGWPSAETECasD3n+5xrzaShUpEQiMtUrrZ+H4XuvW1NZpsPi/1gPxsiFWwojpAlWbWzMCKodVO6Qc\nIx9oSMrhk3y2MJUJK/YwYcUebqsfTL/WEUQGaEfN4pTXv5Z6QI/LSQLAGJMmIsOAdYUZmFL5Vr4q\nNO1nlVN/Wr3Ct06DteNg5ShrHvHojlbSiGgDrnqW6qgiA7wZenddnmofzeeLdjJp1V5+XLufm2tW\non9CJLVDtCFDcchrojgBhAHbsiwPQ+fHVo7Iq2JG09vzp6xmt1unwbbpVn8NV08rWcR0hegOVhJR\nDie4fBleu6UmA9pEMmbpLr5evoeZm/6gVZQf/RMiaRruq533ilBeE8Uk4EsReQ5YhvUwuyXwDtYt\nKaUcl5sXxN5ilUsXrLGotk6zPduYBuJsTQEb09VqQVUu2N4Rqyz8vN15tkMN+raO4JsVe/lyyU7u\n+3wFDaqWp39CJG1jAjRhFIG8JornsPpOfGXbVoDzwEjgX4UbmlJFyNnVmkMjIhE6vm918ts61Uoa\nMwZbJahBRgsq/+r2jlhlUtbDlccSIujZIpQfVu/js4U7efTr1dSo5MNjCRF0rl0ZF+28V2jy2uHu\nPDBQRF7A6o0tQIox5nRRBKdUsXBygpCGVmn3GhzZntHsdt5/rFIxKiNpBDWwtlF25+HqzIPNQrm3\ncVWmbjjIiKRUBk5az39nW5337mwQop33CsF1E4WI/JyLdQAwxtxSCDEpZV/+0eD/NLR6Gk4cgG22\nFlTLPoElH1jzh9fobJXQVtbVibIrV2cn7mgQwm31gpmTfIgRC1J48X+b+GjuDh5tFUb3JtXwdteW\nbvmVm9/cn0UehVKOqlywNeJt495w5i/YPtu6RbV+ojVUukc5iOpgXW1EtrOegyi7cXISOtSsxE2x\ngSxL/ZPhC1J4a8ZWhi9I5eHmofRsHkoFLzd7h1niXDdRGGN6FkcgSjm8MhWg7j1WuXAGUhfYWlDN\nhI3fg4sHhCdaSSO6o9XiStmFiNAi0o8WkX6s33ecEQtS+HjeDj5ftJPuTarSu1U4lcrpwJK5pddi\nSuWHaxmo0ckqly7C3uUZLai2zwRxsjr2Xe4ZXv5a082rolavSnlGPxTH9kP/8FlSKmOX7ebr5bu5\ns0EIfVtHEOanV4HXo4lCqYJydoGwVla5+R34fUNG0vjlX1apVMdqdlujMwTE6nAidhAd6MOwe+rx\nVPtoRi/ayXer9/H96n10ql2ZxxIiqBmknfeuxWEThYh8DrQBgoCTWP02/mWMSbZrYErlRASC6lml\nzUvWFLCXk8aCt2DBm1AhLKMFVUhjbUFVzKr4evL6bbV4om0kXy3ZzYQVe5j22+8kVvenf2IkjUJ1\nZsWsHDZRAKuBr4F9gC/wGjBXREKNMRfsGZhSuVYxAloMtMo/hzJaUK34zGpF5RVgu4XVxRou3UVH\nSS0uAT4e/KtjDR5LiGD88t18tXQ3d3+2nMahvjyWGEFCtL923rNx2ERhjBmV6eVuEXkJ2ACEc/UQ\nIko5Pp9AiOtplbMnYMccK2lsnAxrxoKbD0TfZN2eimwPHmXtHfENoVwZVwa0ieKRluFMWrWXzxft\npOeYVcRWLkv/xAg61qqM8w0+VasYY+wdw3WJiBfwBnAHEG2MOZfNOn2w5vMmMDCw4aRJk/J1rJMn\nT+LtrSNTOprSXC9Ol85T/vhv+B1dgd/RlbhdOEGauPBXhboc9WvKUb/GXHArb+8wr1Ja6+RimmH5\nwYtM33WBP04ZAj2FTuGutAhywaUEJIyC1EtiYuIaY0xc1uUOnShEpD/wHuCFdRXRxRiTcr3t4uLi\nzOrVq/N1zKSkJBISEvK1rSo6N0y9pF2CfSutK43kqXB8DyBQpUnGcw3fMHtHCZT+OrmUZpi1+Q9G\nJKWw6cDfVCrrQe/4cO5rXAVPN4e9GVOgehGRbBNFsT5FE5E3RMRcpyRk2uQboD7QGtgO/CAinsUZ\ns1LFyskZqjWDDm/CwA3Qbykk/AsunILZL8HH9WBEc+vB+O+/gQOf6JV0zk5Cp9qVmTqgJeN6NaZa\nRU9en7aFFu/M5+N5Ozhx+sZ5VFrcafFDYMJ11tl7+QdjzAmsoc13iMgK4C/gTmB8UQWolMMQgUq1\nrJLwL/hrt22k2+nWZEwL37X6Z9Sw9dWo2sxKNKpQiQito/1pHe3Pmj3HGLEglWFztjNqYSoPNK3G\nIy3DCChbujvvFWuiMMYcBY7mc3OxFW0Wom5MFUKh2eNWOXnE6ti3dTqs+hJWjADPilC9o5U4whPB\ntXR/edlDw2q+fNnDl+Tf/2ZkUiqfL97JmGW7uathCP3iI6hasXTe8HDIG20iEol15TAXOAKEYA1j\nfg6YZsfQlHIM3v7Q4CGrnPsHUuZaSWPLz7BuArh6QVQ7K2lE3QRlyts74lIlpnJZPr6vPk+3j2bU\nop1MXr2fSSv30rVuEI8lRFCjUulqseaQiQIrISQAzwDlgUPAIqCZMeYP+4WllANy94Gat1vl4nnY\nvdjWyW8GbPk/cHKxRrmN6QLVO0PZyvaOuNQI9fPi7TtqM6hdFF8s3sk3v+7l/9YfpF1MAP0TI2lQ\ntXTMmOiQicIYsw/oaO84lCpxXNwgsq1VOv0XDqyxRrtNngbTn7FKSCPbMOldwS/S3hGXCoFlPXix\ncyz9EyIZt3w3Y5ft5o4Ry2ga7svjiZG0jPQr0Z33HDJRKKUKgZMTVGlklXZD4Mi2jKQx9zWr+FXP\nNCFTfR2DqoAqeLkxqF00vVuF8+3KvXy+eCcPfrmS2sHleDwxgptiK+FUAvpiZKWJQqkbgQgE1LBK\n/LNwYn/GXOFLPoTF/4WywRkTMlVroRMyFYCXuwuPtgrnwWbV+GntAT5bmEq/CWuJ8PfisYRIbq0X\nhGsJmqpVE4VSN6JyIdCkr1VOH4Ptv1iJY+14WDkaPMrbWlB1hoi24FY6W/MUNXcXZ+5rXJW7G4Yw\nY9MfjFiQwuAfNvDBnO30iQ+nW1wVyrg5fpNmTRRK3eg8faFed6ucPwWp862ksW0mbPgWXMpYzzxq\ndIbom631VZ64ODtxS90gutapzIJthxmxIJVXf97Mx/N20KtlGA82q0ZZD8e9gtNEoZTK4OZlzZsR\n0xUuXYA9SzM6+W2dBuIMoS2gRhfcz2rCyCsRoU2NQNrUCGTlrmMMX5DC+7O28VlSKg80q0avFmH4\n+zheVzFNFEqp7Dm7QniCVTq+BwfX2cagmgYzn6MZwN4RtsRyi7agyqPGYb40DmvMpgMnGJmUymcL\nU/lqyS7uaVSFPvHhhFRwnNt9miiUUtcnAsENrNL2FTi6g9QZHxNxbjPMG2IV/xirBVVMV2tGP21B\nlSu1gssx/P4GpB45yaiFqXy7ci8Tf93LLfWC6J8QQWSAj71D1EShlMoHvyj2Vb2TiIRPMlpQJU+1\nWk8tet8agyrmFitp6Cx+uRLh7817d9VlULtoPl+8k29X7uV/6w5wU2wg/RMiqVulvN1i00ShlCqY\nzC2oTh21HoInT7VaTy3/1DaLX2craYS2sjoFqmsKKl+GV7vWZEBiJGOX7Wbcst3M2nyIlpF+9E+M\noFl4xWLvvKeJQilVeLz8oMGDVjn7N+yYbSWN376HNWPAoxxEd7SSRkQbbXabg4re7jxzU3X6xIcz\n8de9fL54F90//5V6VcrTPyGCdjGBxdZ5TxOFUqpoeJSF2ndZ5cIZ2JlkJY1tM+C3SVaz26h21i0q\nHbjwmnw8XOnbOoKHm4fyw5r9jFqYSp/xa4gO9KZ/QiRd6lTGpYg772miUEoVPdcyVge+6h0zmt0m\nT814tuHkCuGtM+bW8A6wd8QOx8PVmQebVuO+RlWY9tvvjEhKYdB36/nvnG30iY/g7oYheLgWTec9\nTRRKqeJ1RbPb962BC5N/thLGtEEw7SlrEqaYrlYrqvJV7RywY3FxduK2+sHcUjeIucmHGJGUystT\nNvHxvB14uDjhbs5S2DPUaqJQStlP5oEL2/8HDm3OmC981gtWqVzP1uz2FvCvbu+IHYaTk3BTzUq0\njw1k+c4/GbEglSUpRwnyKvznFpoolFKOIevUr3+mZnTwm/+GVfyiM3qOV66nfTWwens3j/CjeYQf\nXT5ezPnT/xT6MTRRKKUcU8UIaDHQKn8fzHiecXm023JVrGcaMV2halOdLxyY9mQrkpKSCn2/miiU\nUo6vbBA07m2Vy6PdJk+F1V/BryPB0y+jr0ZYPLg43nhJJZkmCqVUyZJ5tNtzJyFljpU0Nv0Ea8eB\ne1mI7mAljch21kCHqkA0USilSi5370zzhZ+DnQutFlTbZsDGH8DFw0oWNbpA9ZuhTOmYw7q4aaJQ\nSpUOLu4QfZNVLl2EvcttfTWmWcXJxRpCJKardZvKp5K9Iy4xNFEopUofZxcIa2WVju/CwbVW0tjy\nM0x/GqY/A1Ua25JGF/ANs3fEDk0ThVKqdBOB4IZWafsqHNlqJY3kqTD7JatUqp0x2q1/DW12m4Um\nCqXUjUMEAmKs0vo5+Gu31U8jeSoseAsWvAm+ERmTMQU30KSBJgql1I2sQig0H2CVfw7BNltfjeWf\nwtIPoWywra9GF6ja3LqldQO6Mf/XSimVlU8gxPWyypm/YPssK2ms/RpWjoIyvlCjk3WlEZ5wQ/XV\n0EShlFJZlakAde+1yvlTkDIv42H4ugng5mO1rqrRBaLag7v9pystSpoolFIqJ25eEHuLVS6eh12L\nYKttiPRNP4KzuzUJU0xXaxh1T197R1zoNFEopVRuubhZky1FtYPOw2DfrxktqLbPBHGG0BbW7aka\nna2hR0oBTRRKKZUfTs5QrblVOrwFv2/ISBozBlslpFFGX42KEfaOON80USilVEGJQFA9q7R9GY5s\ny+gVPucVqwTUzBgiPbBmiWp2q4lCKaUKm391q8QPhuN7M4ZIX/guLHwHKoRlJI3gOGsCJwemiUIp\npYpS+arQ9DGrnDxsDViYPA1WjIRlH4N3JdsMfl2hWgtrqlgH4/CJQkQEmAl0AO42xky2c0hKKZU/\n3gHQsIdVzp6A7bOt0W7XT4RVX1jNcqt3sp5pRCSCaxl7RwyUgEQBPANcsncQSilVqDzKQZ27rXLh\nDKTOz3iusf4bcPWy+mjEdIWom8CjrN1CdehEISJxwECgIXDIzuEopVTRcC1jNaet0RkuXYDdi63b\nU1unwZYp4Oxm9QaP6WpdcXj5FWt4DpsoRMQH+Bboa4w5LCWohYBSSuWbs6vVgS+iDXQaCvtXWben\nkqfCjtkgA61nGZfn1SgXUuQhiTGmyA+SHyLyDXDMGPOE7bUhh2cUItIH6AMQGBjYcNKkSfk67smT\nJ/H29s5f0KrIaL04Hq2TYmYM3id34Xd0BX5HV+B9ag8Af/tEcdSvKUf8m3HGM7hA9ZKYmLjGGBOX\ndXmxJgoReQN48TqrJQJVgOeBOGPMWdu2OSaKzOLi4szq1avzFWNSUhIJCQn52lYVHa0Xx6N1YmdH\nU6yhRJKnwoE11jJXT/5xC8Tn2fX52qWIZJsoivvW04fAhOussxfoAcQCJ7PccvpORJYbY1oWSXRK\nKVVS+EVCy6escuKA1Vdj/hsYcS70QxVrojDGHAWOXm89EXkRGJpl8UZgMPB/RRCaUkqVXOWCoUkf\naNKHtUlJJBTy7h3yYbYx5gBwIPMy25XFPmPMTrsEpZRSNyjH7jeulFLK7hzyiiI7xhhtH6uUUnag\nVxRKKaVypIlCKaVUjjRRKKWUypEmCqWUUjnSRKGUUipHmiiUUkrlyGEHBSwIETkC7Mnn5n7kovd4\nKVIOOGHvIHLB3vVSnL+nojxWYe67MOqkpHz+SpKC1Es1Y4x/1oWlMlEUhIiszm5QrNJKREYbY/rY\nO47rsXe9FOfvqSiPVZj7Low6KSmfv5KkKP5W9NaTmmrvAEqI4vw9FeWxHK2+HS0elQ1NFDc4Y4z+\noeZCcf6eivJYjlbfjhaPyp4miquNtncAKltaL45H68QxFXq96DMKpZRSOdIrCqWUUjnSRKGUUipH\nmihUoRKRTiKyTUR2iEh/e8ejtE5UwWmiAETkBRFZJSJ/i8gREZkqIrXsHVdJIyIuwEdAO6AuMEBE\nKhfSvv8tIkZEPi2M/d0oCrtORMRZRF4XkV0ictb27xu246hiICLxIvKziByw/U30yGadaBH5SUSO\ni8hpEVkrIjH5PaYmCksCMAJoDrQBLgJzRcTXnkGVQI2BLcaYfcaY08D/gC4F3amINAV6A78VdF83\noMKuk+eBx4EngRrAQNvrFwoaqMo1b2AT1u/+TNY3RSQMWArswvo+qwW8BJzM7wE1UQDGmA7GmDHG\nmE3GmI3Ag4A/0MLOoSEij4vIb7arnb9FZLmIdC6C41z3LMW2Xv9MZ5NrRKRVpreDgH2ZXu8HggsY\nVzngG+AR4K+C7KswiUhlERlnuwI9KyJbRKR1IR/DEeukOTDVGDPVGLPbGPMz8DPQpAD7VHlgjJlh\njPm3MWYykJbNKm8Cs40xzxhj1hpjdtq22ZfNurmiiSJ7Pli/G0f4YtqPdRbXAIgD5gNTRKROdiuL\nSHMRcc9meZiIhOZwnBzPUmz7uAfrNsZbQH1gGTBTRKpeXiWbzQra/no0MNkYM7+A+yk0IlIe64xN\ngM5ADPAEcPga65emOlkCJIpIDdvxY7HOWmcUYJ+qkIiIE9AV2CIiv9hOZFbZPif5Z4zRkqUA3wPr\nAGd7x3KN+I4BfbNZLsAarGERXDMtrwrsBt7L5f5PAj2yWf4r8HmWZTuAt20/Nwf+L9N7bwK9C/D/\n7G37/7jZXicBnzrA7/8tYGku1y1tdSK2faQBF7CSzhv2rpMbtWT9XACVbHVyCngaqGf79yLQJb/H\n0SuKLERkGNASuNMYc8ne8WRme5B4L9aZ5rKs7xvrk9IJiAK+ExEXEQkBFgArKMB9ZBFxAxoCs7O8\nNRvrywhgJVBTRKqISBngdmB6Po9XHesL+X5jzPn8RV1kbgN+FZHvROSwiKwXkQEictXZe2mqE5t7\ngIeA7lhXuQ8B/UXkkQLsUxWey9/p/2eMGWaMWW+MGYZ18vt4fneqLRUyEZEPgHuBRGPMTnvHc5mI\n1AaWAx5YZxC3G+tZylWMMYdEpA2wEOvDUQtYDzxQwMTnBzgDh7IsP4TVogZjzEUReQqYh/WB/cgY\nczCfx2tmO+amTN+/zkC8iPQDvIwx5/K574IKB/oDHwDvYJ21fWJ776pWWaWoTgDeB4YaYybZXm8U\nkWpYCe/LAuxXFY6jWFcPW7IsT8b6bssXTRQ2IvIR1i8ywRiz1d7xZLEN68uoPHAnME5EEowxm7Jb\n2Rhz0HblsRr4Heus/GIhxZL1/rZkXmasQd4KY6C3KVjxZzYG67bKW4A9rzKcgNXGmMtXA+tEJArr\njC3b5rulpE4APIGsye0S+rzTIRhjzovIKqB6lreiyf8cPZooAERkOFZLp9uAv0Skku2tk8aYfDcp\nKyy2Wy8ptperRaQR8BRWS6CriIgf1pfqL0As8JmI9DLGZNdCIreOYn0hVMqyPICrz2gLzBhzHDie\neZmInAKOXStBFqPfyf6MbeC1NigNdWIzFfiXiOwCNmM9QH8a+LqIjqeyEBFvINL20gmoKiL1sP42\n9gLvAd+LyGKsxi+JWCfBt+X3mHoWYOmP1dJpHtaXwOUy2J5B5cAJuKoVDYCt78cc4CDWByMRaAuM\nzu4eem7ZktUaoH2Wt9qTzfOSUm4peThjK2V18gQwGavfUTLwX+Bz4MUiOp66WhxWY5t1QBlgiO3n\n/wAYY6YAfbC+vzZi1dlDxpj8P5uy91N7Lddt1fAO0AoIBWoDb2O1OOmYzbqC9SxjLuCRaXkUcAB4\nM4fjeGPd3qoHnAZesf1cNdM692Dd8nkUq0noR1jPTKrZ+/dUzHXSCKvFz4tYZ3Z3Y03n+bjWiZbS\nWOwegJbrVBCMxTpTPYfVTn8u0CGH9dsBntksrwFE57BdAtZ97axlbJb1+mM16zyHdTYbb+/fkZ3q\npTOwATgLbMfqqSxaJ1pKY9H5KJRSSuVIn1EopZTKkSYKpZRSOdJEoZRSKkeaKJRSSuVIE4VSSqkc\naaJQSimVI00USimlcqSJQqliICJJovN9qxJKE4VSSqkcaaJQSimVI00UStmBiLQVkeMi0tfesSh1\nPTofhVLFTETuxJqb4lFjzPf2jkep69ErCqWKkYj0Ab4C7tIkoUoKHT1WqWIgIklABNZsdPHGmOX2\njUip3NMrCqWKz29YMyc+UpCZ7ZQqbpoolCo+u7AmI7qJAk6DqlRx0kShVDEyxuzEmjP7ZjRZqBJC\nE4VSxcwYk4p1ZXEzMEqThXJ0+jBbKaVUjvSKQimlVI40USillMqRJgqllFI50kShlFIqR5oolFJK\n5UgThVJKqRxpolBKKZUjTRRKKaVy9P8ZLvDNFxxIAgAAAABJRU5ErkJggg==\n",
            "text/plain": [
              "\u003cFigure size 600x400 with 1 Axes\u003e"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "for quant_name, metrics in vary_num_levels_metrics.items():\n",
        "  distances = jnp.array(metrics['distances'])\n",
        "  distances = jnp.log2(distances)\n",
        "  plt.errorbar(\n",
        "      metrics['num_levels'],\n",
        "      jnp.mean(distances, axis=1),\n",
        "      jnp.std(distances, axis=1),\n",
        "      label=quant_name)\n",
        "plt.legend()\n",
        "plt.xlabel('k')\n",
        "plt.ylabel('log_2(RMSE)')\n",
        "plt.xscale('log')\n",
        "plt.xticks(\n",
        "    ticks=metrics['num_levels'], labels=[f'{s}' for s in metrics['num_levels']])\n",
        "plt.grid()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "wTRLRrJI5MQo"
      },
      "source": [
        "### Effect of random rotation"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 49,
      "metadata": {
        "executionInfo": {
          "elapsed": 16398,
          "status": "ok",
          "timestamp": 1658264843366,
          "user": {
            "displayName": "",
            "userId": ""
          },
          "user_tz": 240
        },
        "id": "Iexcvoiq5N3j",
        "outputId": "cba808cf-f988-4eac-9aee-22491e896303"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "average iteration duration = 0.007363462448120117 sec\n",
            "Finished Independent num_clients=10\n",
            "average iteration duration = 0.020679330825805663 sec\n",
            "Finished Independent+Rotation num_clients=10\n",
            "average iteration duration = 0.011248159408569335 sec\n",
            "Finished Correlated num_clients=10\n",
            "average iteration duration = 0.025571107864379883 sec\n",
            "Finished Correlated+Rotation num_clients=10\n",
            "average iteration duration = 0.014239072799682617 sec\n",
            "Finished Independent num_clients=20\n",
            "average iteration duration = 0.042512011528015134 sec\n",
            "Finished Independent+Rotation num_clients=20\n",
            "average iteration duration = 0.02191011905670166 sec\n",
            "Finished Correlated num_clients=20\n",
            "average iteration duration = 0.04720735549926758 sec\n",
            "Finished Correlated+Rotation num_clients=20\n",
            "average iteration duration = 0.022398042678833007 sec\n",
            "Finished Independent num_clients=40\n",
            "average iteration duration = 0.06663603782653808 sec\n",
            "Finished Independent+Rotation num_clients=40\n",
            "average iteration duration = 0.03333437442779541 sec\n",
            "Finished Correlated num_clients=40\n",
            "average iteration duration = 0.07928609848022461 sec\n",
            "Finished Correlated+Rotation num_clients=40\n",
            "average iteration duration = 0.045046234130859376 sec\n",
            "Finished Independent num_clients=80\n",
            "average iteration duration = 0.14028193950653076 sec\n",
            "Finished Independent+Rotation num_clients=80\n",
            "average iteration duration = 0.06746737957000733 sec\n",
            "Finished Correlated num_clients=80\n",
            "average iteration duration = 0.16326193809509276 sec\n",
            "Finished Correlated+Rotation num_clients=80\n",
            "average iteration duration = 0.09440994262695312 sec\n",
            "Finished Independent num_clients=160\n",
            "average iteration duration = 0.2811218500137329 sec\n",
            "Finished Independent+Rotation num_clients=160\n",
            "average iteration duration = 0.13273804187774657 sec\n",
            "Finished Correlated num_clients=160\n",
            "average iteration duration = 0.3160062551498413 sec\n",
            "Finished Correlated+Rotation num_clients=160\n"
          ]
        }
      ],
      "source": [
        "def run_vary_num_clients(seed, num_clients_range):\n",
        "  rng, mean_rng, quant_rng = jax.random.split(jax.random.PRNGKey(seed), 3)\n",
        "  dimension = 1024\n",
        "  mean = jnp.zeros(shape=[dimension])\n",
        "  mean = mean.at[0].set(1.)\n",
        "  mean = mean.at[1].set(-1.)\n",
        "  sigmamd = 0.01\n",
        "\n",
        "  vary_num_clients_metrics = {}\n",
        "  for num_clients in num_clients_range:\n",
        "    quantizers = get_quantizers(\n",
        "        quant_rng,\n",
        "        num_levels=2,\n",
        "        num_clients=num_clients,\n",
        "        thresholds=jnp.array([0., 1. + 4 * sigmamd]),\n",
        "        update_thresholds=True)\n",
        "    # Just independent and correlated.\n",
        "    quantizers = only_independent_and_correlated_and_rotation(quantizers)\n",
        "    for quantizer_name, quantizer_agg in quantizers.items():\n",
        "      if quantizer_name not in vary_num_clients_metrics:\n",
        "        vary_num_clients_metrics[quantizer_name] = {\n",
        "            'num_clients': [],\n",
        "            'distances': [],\n",
        "        }\n",
        "      vary_num_clients_metrics[quantizer_name]['num_clients'].append(\n",
        "          num_clients)\n",
        "      distances = run_dist_mean(\n",
        "          quantizer_agg,\n",
        "          mean,\n",
        "          sigmamd,\n",
        "          num_clients,\n",
        "          rng,\n",
        "          dimension,\n",
        "          num_iters=10)\n",
        "      vary_num_clients_metrics[quantizer_name]['distances'].append(distances)\n",
        "      print(f'Finished {quantizer_name} num_clients={num_clients}')\n",
        "  return vary_num_clients_metrics\n",
        "\n",
        "\n",
        "vary_num_clients_metrics_rot = run_vary_num_clients(\n",
        "    seed=47, num_clients_range=[10, 20, 40, 80, 160])"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 50,
      "metadata": {
        "colab": {
          "height": 293
        },
        "executionInfo": {
          "elapsed": 408,
          "status": "ok",
          "timestamp": 1658264843929,
          "user": {
            "displayName": "",
            "userId": ""
          },
          "user_tz": 240
        },
        "id": "gkGRDZaMH4Pt",
        "outputId": "b6ce7182-32eb-4677-8335-9330594effea"
      },
      "outputs": [
        {
          "data": {
            "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYoAAAEUCAYAAAA1EnEjAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90\nbGliIHZlcnNpb24zLjMuNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8QVMy6AAAACXBIWXMAAAsT\nAAALEwEAmpwYAABd6klEQVR4nO2deXiU1dn/P2fWLDMJCdkTkpAQshDC6oKKBETRonWrWkUrWLda\n+v60orZaFdzqQn1r61L1LWAR3C0KKCpIwAVlRyAJkABBIEAChJB9mfP745l5ZiYJIYGQTJLzua7n\nmsx5ljnPycx8577Pfe5bSClRKBQKheJEGLq6AwqFQqHwbZRQKBQKhaJVlFAoFAqFolWUUCgUCoWi\nVZRQKBQKhaJVlFAoFAqFolWUUCgUCoWiVbqNUAghHhZCSCHEy13dF4VCoehNdAuhEEKcC9wB/NTV\nfVEoFIrehs8LhRAiGJgH/BY42sXdUSgUil6HzwsF8AbwoZTy667uiEKhUPRGTF3dgdYQQtwBDABu\nacOxdwJ3Avj7+4/o16/fKb2mw+HAYOgO+ukbqPFqH2q82ocar/ZzOmO2ffv2UilleNN2nxUKIUQq\n8AwwWkpZd7LjpZRvoFkfjBw5Uq5du/aUXjcnJ4fs7OxTOrc3osarfajxah9qvNrP6YyZEKKopXaf\nFQpgFBAGbBFCuNqMwIVCiLuBQCllbVd1TqFQKHoLviwUC4CmZsFsYAeapXFSK0OhUCgUp4/PCoWU\nsgwo82wTQlQCR6SUW7qiTwqFQtEbUbNECoVCoWgVn7UoWkJKmd3VfVAoFIreRrcSCoWivLycQ4cO\nUV9f39VdOSWCg4PJy8vr6m50G9R4tZ8TjZnZbCYiIoKgoKB2X1MJhaLbUF5ezsGDB4mNjcXf3x+P\naLhuw/Hjx7Hb7V3djW6DGq/209KYSSmprq5m3759AO0WCzVHoeg2HDp0iNjYWAICArqlSCgUXYUQ\ngoCAAGJjYzl06FC7z1dCoeg21NfX4+/v39XdUCi6Lf7+/qfktlVCoehWKEtCoTh1TvXzo4RC0Su5\n4fVV3PD6qq7uhkLRLVBCoVB0Ay6//HImT57c1d1oM5mZmUyfPr2ru6HoIJRQKBRnmMmTJ3P55Zd3\ndTe6PdOnTyczM7Oru9ErUUKhUCgUilZRQqFQdCJ33303l19+OS+99BKxsbGEhIQwZcoUqqqq9GOq\nqqqYPHkyNpuNyMhInnnmmWbXqaur46GHHiIuLo7AwEDOOussvvjiC31/Tk4OQggWLVrE0KFD8fPz\nY8SIEaxbt87rOt9//z1jxozRQyd/97vfUV5eru/Pzs7mnnvu4eGHHyYsLIyIiAimTZuGw+HQjzl0\n6BBXXnkl/v7+JCQkMGvWrGb9PXbsGHfeeScRERHY7XbGjBmDZymAOXPmYLPZWLZsGZmZmQQGBjJ2\n7Fh2796t758xYwZbt25FCIEQgjlz5rR7/BWnhhIKDwoOVbCzrJHqusau7oqiB/PNN9+wZcsWli5d\nynvvvcd///tfXnrpJX3/tGnT+Oqrr/joo49YtmwZGzZsYOXKlV7XmDJlCitWrGD+/Pls3ryZW2+9\nlSuuuIJNmzZ5HTdt2jSee+451q5dS1JSEhMnTtRFafPmzVxyySX88pe/ZNOmTXz88cds3LiR2267\nzesa8+bNw2Qy8f333/Pyyy/z97//nffee0/fP3nyZAoKCli6dCkLFizgP//5j/4FD9pir4kTJ7Jv\n3z4WLVrEhg0buPDCCxk3bhzFxcX6cbW1tfz1r39l1qxZrFq1irKyMu69914AbrjhBu6//35SU1Mp\nLi6muLiYG2644bT+D4q2o1Zme3DTmz9w6HgtT/64hP59A0mPDiItyq49RtuJ7dM9VwP3ZGYs3Eru\n/vKTH9iE3GLtnFOJfMqICeLxKwa1+zwXQUFBvPbaa5hMJtLT07nuuutYtmwZf/7zn6moqODf//43\ns2bNYsKECQDMnj2buLg4/fzCwkLeeecddu/eTXx8PABTp05l6dKlvP7667z66qv6sY8++miz68yf\nP5/bb7+dF154Qf8CdvHaa68xbNgwDh06REREhHa/GRk88cQTAAwcOJA333yTZcuWceONN7J9+3Y+\n//xzvv32W84//3wA3nrrLZKSkvRrLl++nI0bN1JSUqKvg3nyySdZuHAhc+fO5cEHHwSgoaGBV155\nhdTUVEATuSlTpuBwOPD398dms2EymYiKijrlsVecGkooPPhk6vnMX/Idxr7x5BcfZ8v+Yyze7P7F\nY/czkR4VRHq0nbToINKjgxgYaSPAooZR0XYyMjIwmdzvmZiYGH788UdAE4G6ujpGjRql77fZbAwe\nPFh/vn79eqSUZGRkeF23traWcePGebW1dJ3c3FwA1q1bR0FBgZd1IKXU++ESiqysLK9rxsTE6Kt7\n8/LyMBgMnH322fr+hIQEYmJi9Ofr1q2jqqqK8HDvCps1NTUUFhbqz61Wqy4Srtepr6+nrKyM0NBQ\nFF2H+obzIDrYnxGRJrKzB+ptlbUN5B84Tv6BcvKKy8kvPs5H6/dRUatVDBQC+vcNJC3aTnpUkFNA\nlPXRWZzqL3uXJfHeXaNOcmTHYzabvZ4LIXSfv+uLujUcDgdCCNasWdPsWu1Zue5wOLj99tu57777\nmu2LjY3t0P5GRkbyzTffNNvnmXPIUzxdr+M6X9G1KKE4CYFWEyMSQhiREKK3ORySfWXV5DqFI6+4\nnNz95Xy2+YB+jMv6SIu26y6s1Ci7sj4UrTJgwADMZjM//PCD7r6prKxky5YtJCcnAzBs2DCklBw4\ncICxY8e2er2WrvOb3/wGgOHDh7N161YGDBhwyv1NT0/H4XCwZs0azjvvPAD27NnD/v379WOGDx/O\nwYMHMRgMXi6p9mKxWGhsVPOHXYH61joFDAZBv9AA+oUGMGGQ219aWdvAtoPHdcsjr7icj5tYH4l9\nAzXXVVSQLiBxIcr6UGjYbDZ++9vf8tBDDxEeHk5MTAxPPPGE1xfkwIEDmTRpEpMnT+Zvf/sbw4cP\n58iRI+Tk5JCUlMQ111yjH/vUU095XcdisXDTTTcB8NBDD3Huuedy9913c9ddd2G328nPz2fhwoW8\n/vrrbepvamoql156KXfddRdvvPEG/v7+/PGPf/SybMaPH8/555/PlVdeyfPPP09aWhoHDhxgyZIl\njB8/ntGjR7fptRITEykqKmL9+vXEx8djt9uxWq1tOldxeiih6EACrSaGx4cwPN5tfUgp2Xu0mrzi\ncvKKNRdW7v5yPt9yAJfVbreaSPMUj2g7acr66LXMnDmTyspKrr76agICAvjDH/5AZWWl1zGzZ8/m\n6aef5sEHH2Tv3r2EhoZy9tlnN7Mwnn32We6//362bdvGoEGDWLRoEYGBgYA297By5Ur+8pe/MGbM\nGBobG0lKSuLqq69uV3/nzJnDHXfcwbhx4wgLC+Pxxx/3ylAqhOCzzz7jL3/5C3fccQeHDh0iMjKS\n888/X7du2sK1117Lxx9/zEUXXURZWRmzZ8/uVqvVuzOiLT7G7sbIkSOlZ4x2e8jJySE7O7tjO9QC\nLuvDZXnkH9CskOO1DYBmfSSEBjitDm3eIz06yOesj84aL9AmTtPT0zvkWl01R9FZ9RVycnIYO3Ys\nJSUlhIWFnfHXO1OoehTt52Rj1trnSAixTko5smm7+snaRZzM+sg/cFx/XLLV2/pI9QjZTY8OIjXS\nTqBV/SsVCsWZQX27+BBCuOc+Lmky97H94HHddZVXXM6CDfs4/oO39eHpusrwQevDl+iKaCeForui\nhKIbEGg1MSw+hGEtWB9uy0ObA/ki12192Kwm0qLsHpFX2uS5sj56PtnZ2W0KXVUo2oL6xuimeFof\nF2dE6u1VdQ1sO3DcLSDFx/lkw37e/mGPfkxC3wCv0N30KM36MBiU9aFQKJqjhKKHEWBp2frYV1at\nua6Ky8lzTpw3tT60uQ939FVqlB2bsj4Uil6P+hboBQghiAsJIC6kufWx/WCF0/LQXFefbNzP2zXe\n1oee7yoqSJ/7UNaHQtF7UELRiwmwmBjarw9D+/XR205kfXyZe1C3PgItRtKig/j5SBWisY5XEo+Q\nGmXH7mdu+YUUCkW3RgmFwov2WB+HK+todEh+9S9tTUJciL++5iM1SnNhJfYNwGRU2ewViu6MEgpF\nmziR9fHRkuWEJA5yJk7UrJDl2w7R6NDMD6vJwMBIu57rypW2pK+ti1MvzJ6oPU5Z3LX9UCi6AUoo\nFKeMEIIwfwPZ6ZFclO62PmrqGyk4VEH+geNsO6AtGly+rYQP1u3Vjwm3W7XQXaflkRZtZ0CEDavJ\n2BW34vNcfvnlhIWFdZuqbpmZmfzqV79i+vTpXd2VbkFiYiJTp05l2rRpXd2VFlFCoehw/MxGMmOD\nyYwN9movrahlm8eK8/wD5by1qoi6Bi2NtNEgSAoLJM1pdWhrQIKICfbr1gsHJ0+eTGlpKYsWLerq\nrnRrpk+fzocffsiWLVtO+Rq7d++mf//++vOgoCDS09N55JFHuOKKK9p8nVP9n57oHtasWaPn4PJF\nlFAoOo0wm5WwAVbOH+DOPdTQ6GD34So911X+geNs2HOUhZvcaapdKdvvOyuAwxW1+JmN+JmNGFXk\nlaIFEhMTmTNnTqs5yJYsWcKQIUMoKyvj1Vdf5dprr2X9+vVkZmZ2Xkc9aFrUyddQs4yKLsVkNDAg\nwsblWTFMm5DK/906km8fGsdP0y/hw7tH8dRVmVw5NAaHlFTVNbKvrJrCkgq27j9G/oFydpdWcuBY\nDceq6qitb/T51ch33303l19+OS+99BKxsbGEhIQwZcoUvY41QFVVFZMnT8ZmsxEZGckzzzzT7Dp1\ndXU89NBDxMXFERgYyFlnncUXX3yh78/JyUEIwaJFixg6dCh+fn6MGDGCdevWeV3n+++/Z8yYMQQE\nBBAbG8vvfvc7ysvdpWWzs7O55557ePjhhwkLCyMiIoJp06Z5FRM6dOgQV155Jf7+/iQkJDBr1qxm\n/T127Bh33nknERER2O12xowZg2fizjlz5mCz2Vi2bBmZmZkEBgYyduxYvfb2nDlzmDFjBlu3bkUI\ngRDitNxwffv2JSoqirS0NJ5++mnq6+tZvny5vn/z5s2MHz8ef39/QkNDmTx5MseOHQM0q+Ctt95i\n8eLFel9ycnIA+NOf/kRqair+/v4kJiby4IMPUlNTc9J7SExMZObMmfrr79mzh6uvvhq73Y7dbuea\na65h716363b69OlkZmby7rvvkpycjN1u56qrrqK0tPSUx6Q1lFAofJIgPzMjE0O5+dwEnrpqMB/+\n7jxi+viTFmUnsW8gUUF++JuN1DY4KDleQ9GRKrYdPM7W/eXsOHScvUeqKD1eS0VNPQ2NvlUh7Ztv\nvmHLli0sXbqU9957j//+97+89NJL+v5p06bx1Vdf8dFHH7Fs2TI2bNjAypUrva4xZcoUVqxYwfz5\n89m8eTO33norV1xxBZs2bfI6btq0aTz33HOsXbuWpKQkJk6cqIvS5s2bueSSS/jlL3/Jpk2b+Pjj\nj9m4cSO33Xab1zXmzZuHyWTi+++/5+WXX+bvf/+7V/nUyZMnU1BQwNKlS1mwYAH/+c9/9C940IIe\nJk6cyL59+1i0aBEbNmzgwgsvZNy4cRQXu0sN19bW8te//pVZs2axatUqysrKuPfeewH02t6pqakU\nFxdTXFzMDTfccFr/B4D6+nrefPNNwF3Jr6qqiksvvRSbzcbq1av573//y/fff6+Py7Rp07j++usZ\nP3683hdX0abAwEBmzZpFXl4er776Ku+++y5PP/10u+5BSslVV13FwYMH+frrr1m+fDn79+/nqquu\n8vohtHv3bv398+WXX7JhwwYeeeSR0x6TllCuJ0W3wmIyYjEZCfJ3rtn4/E/IAz/hkOCQEodD6n+7\nPlM1aIkTDUJgMGiPpkOaj1i4op/aQ9RguOzZU76HoKAgXnvtNUwmE+np6Vx33XUsW7aMP//5z1RU\nVPDvf/+bWbNmMWHCBECrPREXF6efX1hYyDvvvMPu3buJj48HYOrUqSxdupTXX3+dV199VT/20Ucf\nbXad+fPnc/vtt/PCCy/oX14uXnvtNYYNG8ahQ4f0mtkZGRk88cQTgFY06c0332TZsmXceOONbN++\nnc8//5xvv/2W888/H4C33nrLq5Ld8uXL2bhxIyUlJXpBoyeffJKFCxcyd+5cHnzwQQAaGhp45ZVX\n9LrZ06ZNY8qUKTgcDvz9/bHZbJhMJqKi3AkzT5ULL7wQg8FAdXU1DoeD/v37c/311wOaMFZUVDB3\n7lw9Xfcbb7zB2LFjKSgoYMCAAfj7+2O1Wpv15dFHH9X/TkxM5OGHH2bmzJk8+eSTbb6HpUuXsmnT\nJgoLC0lMTARg/vz5DBgwgGXLljF+/Hh9vObMmUNwsDYXeOeddzJ79uzTHpuWUEKh6PYIBEYBRiG8\nbGQH3sLhkJL6RkBKDM7w3dr6Bk1AhMDgFBMhtGueKTIyMrzqQ8fExPDjjz8CmgjU1dUxapQ7u63N\nZmPw4MH68/Xr1yOlJCMjw+u6tbW1jBs3zqutpevk5uYCsG7dOgoKCrysA9cv1sLCQl0osrKyvK4Z\nExOjFybKy8vDYDBw9tln6/sTEhKIiYnRn69bt46qqqpmfviamhoKCwv151arVRcJ1+vU19dTVlZG\naGgoJ+Kyyy7zqsddVVXFZZddhtHojqCrqKjwOmf+/PkMGjSI7du3c9999/HGG2/or5GXl0dWVpZX\nTYfzzjsPg8FAbm5uq6VjP/zwQ/7+979TUFBARUUFjY2N7S7fmpeXR0xMjC4SAElJScTExJCbm6sL\nRUJCgi4S4P1/6WiUUCi6N638sjfQ3LcqpaS2wYH8z+U4pOTQVR9RU99InYd7ymgQ+oS5n9mAv8mI\ntQMnz10uDhdCCN3n35Y5FofDgRCCNWvWNLuWZwnStlzn9ttv57777mu2LzY2tkP7GxkZ6fVl7iIo\nKEj/21M8Xa/jOr81/u///o/q6mr9eXZ2Ns899xznnHPOCc+Ji4sjJSWFlJQUbDYb1113Hbm5uYSF\nhSGlPGGUXWvRdz/88AO//vWvefzxx/nf//1f+vTpw6efftrukNe2vn5r/5eORgmFolchhCYCGDQJ\nSQzTQhIbHA5q6x3U1DdSXd9ITb2Do5V1ODy+CC0mA/4eAuJnMmIxGTo0dHfAgAGYzWZ++OEH3X1T\nWVnJli1bSE5OBmDYsGFIKTlw4ECz0qdNaek6rvKjw4cPZ+vWra3+Qj4Z6enpOBwO1qxZo/vp9+zZ\n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\nEkLsQnM9DQP+CPwHoLe/x072fQU8D7wvhPgG+BoYC/wa7TOLlPKYEOLfwAtCiENoVu2LaGHbS9vU\nia4O9+qiELNsNN9c022Oc78ApqOF5NUAK4DMru53F41VS+Mkgekex6jxan0Mc3CGx6rxanF87Ghx\n/kVo6wR2os3z+KkxO/n3lfOYyWgCWu0UgBubXMMP+KdTJKrQxLlfW/ugkgIqFAqFolXUHIVCoVAo\nWkUJhUKhUChaRQmFQqFQKFpFCYVCoVAoWkUJhUKhUChaRQmFQqFQKFpFCYVCoVAoWkUJhUKhUCha\nRQmFQqFQKFqlV+Z6Uig6EyFEDlp2zzLgTrTKYv8BHpRSOrquZwpF21AWhULROUxCSwd9HjAVuBet\nDoNC4fOoXE8KxRnGaVFYpZSjPNq+AoqklLd3WccUijaiLAqFonP4qcnz/WiFYxQKn0cJhULROdQ3\neS5Rnz9FN0G9URUKhULRKkooFAqFQtEqSigUCoVC0Soq6kmhUCgUraIsCoVCoVC0ihIKhUKhULSK\nEgqFQqFQtIoSCoVCoVC0ihIKhUKhULSKEgqFQqFQtIoSCoVCoVC0ihIKhUKhULSKEgqFQqFQtMr/\nBydpVcN43CZ9AAAAAElFTkSuQmCC\n",
            "text/plain": [
              "\u003cFigure size 600x400 with 1 Axes\u003e"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "for quant_name, metrics in vary_num_clients_metrics_rot.items():\n",
        "  distances = jnp.array(metrics['distances'])\n",
        "  distances = jnp.log2(distances)\n",
        "  plt.errorbar(\n",
        "      metrics['num_clients'],\n",
        "      jnp.mean(distances, axis=1),\n",
        "      jnp.std(distances, axis=1),\n",
        "      label=quant_name)\n",
        "plt.legend()\n",
        "plt.xlabel('n')\n",
        "plt.ylabel('log_2(RMSE)')\n",
        "plt.ylim(top=4.0)\n",
        "plt.xscale('log')\n",
        "plt.xticks(\n",
        "    ticks=metrics['num_clients'],\n",
        "    labels=[f'{s}' for s in metrics['num_clients']])\n",
        "plt.grid()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "svG42IC8kcCc"
      },
      "source": [
        "### Distributed mean estimation"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 51,
      "metadata": {
        "executionInfo": {
          "elapsed": 915,
          "status": "ok",
          "timestamp": 1658264844984,
          "user": {
            "displayName": "",
            "userId": ""
          },
          "user_tz": 240
        },
        "id": "bNQd7kekkiug",
        "outputId": "a6892d40-a949-4c08-db11-6e99e5fab9e9"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "average iteration duration = 0.0034392833709716796 sec\n",
            "Finished None num_clients=10\n",
            "average iteration duration = 0.006197118759155273 sec\n",
            "Finished Independent num_clients=10\n",
            "average iteration duration = 0.01759061813354492 sec\n",
            "Finished Independent+Rotation num_clients=10\n",
            "average iteration duration = 0.006282424926757813 sec\n",
            "Finished TernGrad num_clients=10\n",
            "average iteration duration = 0.017534089088439942 sec\n",
            "Finished DRIVE num_clients=10\n",
            "average iteration duration = 0.009972429275512696 sec\n",
            "Finished Correlated num_clients=10\n",
            "average iteration duration = 0.022092914581298827 sec\n",
            "Finished Correlated+Rotation num_clients=10\n"
          ]
        }
      ],
      "source": [
        "def run_vary_num_clients(seed, num_clients_range):\n",
        "  rng, mean_rng, quant_rng = jax.random.split(jax.random.PRNGKey(seed), 3)\n",
        "  dimension = 1024\n",
        "  mean = jax.random.uniform(key=mean_rng, shape=[dimension])\n",
        "  mean = jnp.where(mean \u003e 0.99, 1. * jnp.sign(mean - 0.995), 0.)\n",
        "  sigmamd = 0.01\n",
        "\n",
        "  vary_num_clients_metrics = {}\n",
        "  for num_clients in num_clients_range:\n",
        "    quantizers = get_quantizers(\n",
        "        quant_rng,\n",
        "        num_levels=2,\n",
        "        num_clients=num_clients,\n",
        "        thresholds=jnp.array([0., 1. + 4 * sigmamd]),\n",
        "        update_thresholds=True)\n",
        "    for quantizer_name, quantizer_agg in quantizers.items():\n",
        "      if quantizer_name not in vary_num_clients_metrics:\n",
        "        vary_num_clients_metrics[quantizer_name] = {\n",
        "            'num_clients': [],\n",
        "            'distances': [],\n",
        "        }\n",
        "      vary_num_clients_metrics[quantizer_name]['num_clients'].append(\n",
        "          num_clients)\n",
        "      distances = run_dist_mean(\n",
        "          quantizer_agg,\n",
        "          mean,\n",
        "          sigmamd,\n",
        "          num_clients,\n",
        "          rng,\n",
        "          dimension,\n",
        "          dist='punif',\n",
        "          num_iters=10)\n",
        "      vary_num_clients_metrics[quantizer_name]['distances'].append(distances)\n",
        "      print(f'Finished {quantizer_name} num_clients={num_clients}')\n",
        "  return vary_num_clients_metrics\n",
        "\n",
        "vary_num_clients_metrics = run_vary_num_clients(\n",
        "    seed=47, num_clients_range=[10])"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 52,
      "metadata": {
        "executionInfo": {
          "elapsed": 53,
          "status": "ok",
          "timestamp": 1658264845155,
          "user": {
            "displayName": "",
            "userId": ""
          },
          "user_tz": 240
        },
        "id": "ynUQJ2_fmc-D",
        "outputId": "ef886930-0ff8-4999-fac4-0bb5cb083c44"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "None 0.0 0.0\n",
            "Independent 10.279256 0.24624498\n",
            "Independent+Rotation 3.287459 0.19471912\n",
            "TernGrad 2.6432343 0.0067978394\n",
            "DRIVE 0.84225017 0.014251265\n",
            "Correlated 1.401985 0.05315395\n",
            "Correlated+Rotation 1.0122799 0.055120073\n"
          ]
        }
      ],
      "source": [
        "for quant_name, metrics in vary_num_clients_metrics.items():\n",
        "  distances = jnp.array(metrics['distances'])\n",
        "  print(quant_name,\n",
        "        jnp.mean(distances, axis=1)[0],\n",
        "        jnp.std(distances, axis=1)[0])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "44HRISWleYQE"
      },
      "source": [
        "## MNIST (d=784)\n",
        "\n",
        "MNIST dataset with pixels (28, 28) flattened (784)."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 53,
      "metadata": {
        "executionInfo": {
          "elapsed": 5902,
          "status": "ok",
          "timestamp": 1658264851180,
          "user": {
            "displayName": "",
            "userId": ""
          },
          "user_tz": 240
        },
        "id": "XjivBbQgeiVQ"
      },
      "outputs": [],
      "source": [
        "DIMENSION = 784\n",
        "\n",
        "def flatten_pad_normalize(d):\n",
        "  return tf.pad(tf.reshape(d['image'], [-1]), [[0, DIMENSION - 784]]) / 255\n",
        "\n",
        "\n",
        "mnist_ds = tfds.load(name='mnist', split='train')\n",
        "# Read into memory.\n",
        "mnist_x = jnp.array(\n",
        "    list(mnist_ds.map(flatten_pad_normalize).as_numpy_iterator()))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 54,
      "metadata": {
        "executionInfo": {
          "elapsed": 52,
          "status": "ok",
          "timestamp": 1658264851368,
          "user": {
            "displayName": "",
            "userId": ""
          },
          "user_tz": 240
        },
        "id": "oR4PeCeal649"
      },
      "outputs": [],
      "source": [
        "def partition_data(x, k, rng):\n",
        "  partitions = []\n",
        "  num_examples = x.shape[0]\n",
        "  perm = jax.random.permutation(rng, num_examples)\n",
        "  num_per_client = math.floor(num_examples / k)\n",
        "  for i in range(k):\n",
        "    low = i * num_per_client\n",
        "    # In case it can't be split evenly.\n",
        "    if (i + 1) == k:\n",
        "      high = num_examples\n",
        "    else:\n",
        "      high = (i + 1) * num_per_client\n",
        "    partitions.append(x[perm[low:high], :])\n",
        "  return partitions"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "fdqQHXTnt7J2"
      },
      "source": [
        "### Distributed mean estimation"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 55,
      "metadata": {
        "executionInfo": {
          "elapsed": 29666,
          "status": "ok",
          "timestamp": 1658264881159,
          "user": {
            "displayName": "",
            "userId": ""
          },
          "user_tz": 240
        },
        "id": "aLtr12T6uWQw",
        "outputId": "8d840a35-e12a-4600-f2e6-0cedd21c663a"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Average iteration duration = 0.31287455558776855 sec\n",
            "Finished None num_levels=2 seed=47\n",
            "Average iteration duration = 0.3519250154495239 sec\n",
            "Finished Independent num_levels=2 seed=47\n",
            "Average iteration duration = 0.49955313205718993 sec\n",
            "Finished Independent+Rotation num_levels=2 seed=47\n",
            "Average iteration duration = 0.3531170845031738 sec\n",
            "Finished TernGrad num_levels=2 seed=47\n",
            "Average iteration duration = 0.46698195934295655 sec\n",
            "Finished DRIVE num_levels=2 seed=47\n",
            "Average iteration duration = 0.4603892803192139 sec\n",
            "Finished Correlated num_levels=2 seed=47\n",
            "Average iteration duration = 0.4926802158355713 sec\n",
            "Finished Correlated+Rotation num_levels=2 seed=47\n"
          ]
        }
      ],
      "source": [
        "def run_dist_mean2(x,\n",
        "                   aggregator,\n",
        "                   rng,\n",
        "                   num_clients=100,\n",
        "                   num_iters=10,\n",
        "                   verbose=True):\n",
        "  start = time.time()\n",
        "  experiment_metrics = {\n",
        "      'distances': [],\n",
        "  }\n",
        "  # Ground truth mean.\n",
        "  true_mean = jnp.mean(x, axis=0)\n",
        "  # Init aggregator state.\n",
        "  aggregator_state = aggregator.init()\n",
        "  for it in range(num_iters + 1):\n",
        "    rng, client_rng = jax.random.split(rng)\n",
        "    # Split and distribute clients.\n",
        "    clients = partition_data(x, num_clients, client_rng)\n",
        "    local_means = []\n",
        "    local_weights = []\n",
        "    for local_x in clients:\n",
        "      local_means.append(jnp.mean(local_x, axis=0))\n",
        "      local_weights.append(local_x.shape[0])\n",
        "    aggregated_mean, aggregator_state = aggregator.apply(\n",
        "        [(j, local_means[j], local_weights[j]) for j in range(num_clients)],\n",
        "        aggregator_state)\n",
        "    # Distance between current estimated mean and groundtruth mean.\n",
        "    distance = jnp.linalg.norm(true_mean - aggregated_mean)\n",
        "    if it \u003e 0:\n",
        "      experiment_metrics['distances'].append(distance)\n",
        "  end = time.time()\n",
        "  print(f'Average iteration duration = {(end - start)/num_iters} sec')\n",
        "  return {k: jnp.array(v) for k, v in experiment_metrics.items()}\n",
        "\n",
        "\n",
        "def run_dist_mean2_experiment(seed, num_levels_range):\n",
        "  experiment_metrics = {}\n",
        "  for num_levels in num_levels_range:\n",
        "    key = f'num_levels={num_levels} seed={seed}'\n",
        "    experiment_metrics[key] = {}\n",
        "    mean_metrics = experiment_metrics[key]\n",
        "    num_clients = 100\n",
        "    rng = jax.random.PRNGKey(seed)\n",
        "    rng, quant_rng = jax.random.split(rng)\n",
        "    quantizers = get_quantizers(\n",
        "        rng=quant_rng,\n",
        "        num_levels=num_levels,\n",
        "        num_clients=num_clients,\n",
        "        thresholds=jnp.array([0., 1.]),\n",
        "        update_thresholds=True)\n",
        "    for quantizer_name, quantizer in quantizers.items():\n",
        "      mean_metrics[quantizer_name] = run_dist_mean2(\n",
        "          mnist_x,\n",
        "          quantizer,\n",
        "          rng,\n",
        "          num_clients=num_clients,\n",
        "          num_iters=10,\n",
        "          verbose=False)\n",
        "      print(f'Finished {quantizer_name} {key}')\n",
        "  return experiment_metrics\n",
        "\n",
        "\n",
        "experiment_mean_metrics = run_dist_mean2_experiment(\n",
        "    seed=47, num_levels_range=[2])"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 56,
      "metadata": {
        "executionInfo": {
          "elapsed": 68,
          "status": "ok",
          "timestamp": 1658264881367,
          "user": {
            "displayName": "",
            "userId": ""
          },
          "user_tz": 240
        },
        "id": "6VLT6mmOxRXO",
        "outputId": "11f79647-5bf5-4ea9-c717-3036d75d8883"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "None 1.1163191e-06 3.5329478e-08\n",
            "Independent 0.46585464 0.013661128\n",
            "Independent+Rotation 1.6608591 0.12572193\n",
            "TernGrad 0.6213985 0.005817535\n",
            "DRIVE 0.40258288 0.014060988\n",
            "Correlated 0.14114387 0.0041163075\n",
            "Correlated+Rotation 0.23769055 0.011894402\n"
          ]
        }
      ],
      "source": [
        "for quantizer_name, metrics in experiment_mean_metrics[\n",
        "    'num_levels=2 seed=47'].items():\n",
        "  print(quantizer_name, jnp.mean(metrics['distances']),\n",
        "        jnp.std(metrics['distances']))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "QGCfC69vc6_W"
      },
      "source": [
        "### Distributed Lloyd's algorithm (k-means)\n",
        "\n",
        "MNIST (d=784)\n",
        "\n",
        "Each client has access to a subset of data points.\n",
        "In each iteration, the server broadcasts the cluster centers to all the clients.\n",
        "Each client updates the centers based on its local data, and sends the centers\n",
        "back to the server.\n",
        "The server then updates the centers by computing the weighted average of the\n",
        "centers sent from all clients.\n",
        "\n",
        "In the quantized setting, the client compresses the new centers before\n",
        "sending to the server. This saves the uplink communication cost.\n",
        "\n",
        "Number of centers and number of clients is 10."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 57,
      "metadata": {
        "executionInfo": {
          "elapsed": 121,
          "status": "ok",
          "timestamp": 1658264881593,
          "user": {
            "displayName": "",
            "userId": ""
          },
          "user_tz": 240
        },
        "id": "do_HLaWC8Inr",
        "outputId": "ade1e04a-b253-4244-d41d-01833eb6a5b4"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "[[6.        7.2111025 5.9160795]\n",
            " [8.185353  9.539392  8.717797 ]]\n"
          ]
        }
      ],
      "source": [
        "# [num_examples, d]\n",
        "X = jnp.array([[2, 3, 4, 2, 0, 0], [2, 3, 6, 2, 0, 0], [1, 3, 4, 2, 0, 0]])\n",
        "# [num_centers, d]\n",
        "centers = jnp.array([[1, -2, 1, 3, 0, 0], [6, -2, -1, 3, 0, 0]])\n",
        "\n",
        "# Manual calc to check for correctness.\n",
        "# [[6.0, 7.2111025, 5.9160795],\n",
        "#  [8.185352, 9.5393915, 8.717797]]\n",
        "# for c in centers:\n",
        "#   for x in X:\n",
        "#     print(c, x)\n",
        "#     print(jnp.linalg.norm(x - c))\n",
        "# matches behavior when using Matlab's pdist2 with euclidean\n",
        "# https://www.mathworks.com/help/stats/pdist2.html\n",
        "\n",
        "# Due to slow initial compilations for each shape (10 clients), try to run this\n",
        "# cell only once so we can use the cached compilations.\n",
        "# jit for speed\n",
        "distance = jax.jit(\n",
        "    # Outer vmap vectorizes over multiple centers and broadcasts examples.\n",
        "    jax.vmap(\n",
        "        # Inner vmap vectorizes over num examples and broadcasts center.\n",
        "        jax.vmap(\n",
        "            lambda x, center: jnp.linalg.norm(x - center), in_axes=(0, None)),\n",
        "        in_axes=(None, 0)))\n",
        "print(distance(X, centers))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 58,
      "metadata": {
        "executionInfo": {
          "elapsed": 56,
          "status": "ok",
          "timestamp": 1658264881767,
          "user": {
            "displayName": "",
            "userId": ""
          },
          "user_tz": 240
        },
        "id": "BgBYnhE2e7-l"
      },
      "outputs": [],
      "source": [
        "def choose_random_centers(x, k, rng):\n",
        "  num_examples = x.shape[0]\n",
        "  perm = jax.random.permutation(rng, num_examples)\n",
        "  return x[perm[:k], :]\n",
        "\n",
        "\n",
        "def single_kmeans_iter(x, centers):\n",
        "  # x is [num_examples, d]\n",
        "  # centers is [num_centers, d]\n",
        "  # dist is [num_centers, num_examples]\n",
        "  dist = distance(x, centers)\n",
        "  # obj_dist is [num_examples]\n",
        "  obj_dist = jnp.min(dist, axis=0)\n",
        "  # obj_label is [num_examples]\n",
        "  obj_label = jnp.argmin(dist, axis=0)\n",
        "  obj = jnp.sum(obj_dist)\n",
        "  new_centers = []\n",
        "  centers_weights = []\n",
        "  for i in range(centers.shape[0]):\n",
        "    mask = obj_label == i\n",
        "    num = jnp.sum(mask)\n",
        "    if num \u003e 0:\n",
        "      # Recompute center.\n",
        "      new_center = jnp.sum(jnp.expand_dims(mask, 1) * x, axis=0) / num\n",
        "      new_center = jnp.nan_to_num(new_center)\n",
        "      new_centers.append(new_center)\n",
        "    else:\n",
        "      # Keep original center.\n",
        "      # Dumb way to make sure there's no funny business going on with references\n",
        "      new_centers.append(jnp.ones_like(centers[i]) * centers[i])\n",
        "    centers_weights.append(num)\n",
        "  new_centers = jnp.array(new_centers)\n",
        "  centers_weights = jnp.array(centers_weights)\n",
        "  return (new_centers, centers_weights, obj)\n",
        "\n",
        "\n",
        "def choose_kmeanspp_centers(x, k, rng):\n",
        "  rng, use_rng = jax.random.split(rng)\n",
        "  num_examples = x.shape[0]\n",
        "  idx = jax.random.randint(use_rng, shape=(), minval=0, maxval=num_examples)\n",
        "  centers = [x[idx]]\n",
        "  for i in range(k - 1):\n",
        "    rng, use_rng = jax.random.split(rng)\n",
        "    dist = distance(x, jnp.array(centers))\n",
        "    obj = jnp.min(jnp.square(dist), axis=0)\n",
        "    probs = obj / jnp.sum(obj)\n",
        "    idx = jax.random.choice(use_rng, jnp.arange(num_examples), p=probs)\n",
        "    centers.append(x[idx])\n",
        "  return jnp.array(centers)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 59,
      "metadata": {
        "executionInfo": {
          "elapsed": 738582,
          "status": "ok",
          "timestamp": 1658265620476,
          "user": {
            "displayName": "",
            "userId": ""
          },
          "user_tz": 240
        },
        "id": "1ofW7cPb_O3s",
        "outputId": "d2a8d1f9-70bb-480f-ca31-33a85503cc9f"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Average iteration duration = 9.565997743606568 sec\n",
            "Finished None num_levels=2 seed=47\n",
            "Average iteration duration = 8.914821243286132 sec\n",
            "Finished Independent num_levels=2 seed=47\n",
            "Average iteration duration = 13.254502844810485 sec\n",
            "Finished Independent+Rotation num_levels=2 seed=47\n",
            "Average iteration duration = 8.810287237167358 sec\n",
            "Finished TernGrad num_levels=2 seed=47\n",
            "Average iteration duration = 11.446314692497253 sec\n",
            "Finished DRIVE num_levels=2 seed=47"
          ]
        },
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "\n",
            "Average iteration duration = 9.462021088600158 sec\n",
            "Finished Correlated num_levels=2 seed=47\n",
            "Average iteration duration = 12.301705312728881 sec\n",
            "Finished Correlated+Rotation num_levels=2 seed=47\n"
          ]
        }
      ],
      "source": [
        "def run_dist_kmeans(x,\n",
        "                    aggregator_name,\n",
        "                    aggregator,\n",
        "                    rng,\n",
        "                    num_clients=10,\n",
        "                    num_centers=10,\n",
        "                    num_rounds=50,\n",
        "                    num_iters=10,\n",
        "                    verbose=True):\n",
        "  start = time.time()\n",
        "  # Run multiple multiple trials.\n",
        "  metrics = {\n",
        "      'comm_rounds': [],\n",
        "      'round_dist': [],\n",
        "      'round_obj': [],\n",
        "      'centers': [],\n",
        "  }\n",
        "  # Aggregator state per center.\n",
        "  aggregator_states = [aggregator.init() for _ in range(num_centers)]\n",
        "  for it in range(num_iters + 1):\n",
        "    rng, client_rng, center_rng = jax.random.split(rng, 3)\n",
        "    # Split dataset EVENLY into i.i.d. clients.\n",
        "    clients = partition_data(x, num_clients, client_rng)\n",
        "    # # Initialize centers by randomly selecting N points in X.\n",
        "    # centers = choose_random_centers(x, num_centers, center_rng)\n",
        "    # Initialize centers via kmeans++ algorithm.\n",
        "    centers = choose_kmeanspp_centers(x, num_centers, center_rng)\n",
        "    comm_rounds = []\n",
        "    round_dist = []\n",
        "    round_obj = []\n",
        "    for t in range(num_rounds):\n",
        "      # [num_clients, num_centers, d]\n",
        "      client_centers = []\n",
        "      # [num_clients, num_centers]\n",
        "      client_weights = []\n",
        "      # [num_clients]\n",
        "      client_objectives = []\n",
        "      for local_x in clients:\n",
        "        local_centers, local_weights, _ = single_kmeans_iter(local_x, centers)\n",
        "        client_centers.append(local_centers)\n",
        "        client_weights.append(local_weights)\n",
        "      # Aggregate\n",
        "      new_aggregator_states = [None for _ in range(num_centers)]\n",
        "      new_centers = [None for _ in range(num_centers)]\n",
        "      for i in range(num_centers):\n",
        "        new_centers[i], new_aggregator_states[i] = aggregator.apply(\n",
        "            [(j, client_centers[j][i], client_weights[j][i])\n",
        "             for j in range(num_clients)], aggregator_states[i])\n",
        "      centers = jnp.nan_to_num(jnp.array(new_centers))\n",
        "      aggregator_states = new_aggregator_states\n",
        "      dist = distance(mnist_x, centers)\n",
        "      obj_dist = jnp.min(dist, axis=0)\n",
        "      round_dist.append(jnp.mean(obj_dist))\n",
        "      # Objective according to https://en.wikipedia.org/wiki/K-means_clustering\n",
        "      round_obj.append(jnp.mean(jnp.square(obj_dist)))\n",
        "      comm_rounds.append(t + 1)\n",
        "      if t % 10 == 0 and verbose:\n",
        "        print(f'{t} obj: {round_obj[t]} dist: {round_dist[t]}')\n",
        "    if it \u003e 0:\n",
        "      metrics['comm_rounds'].append(comm_rounds)\n",
        "      metrics['round_dist'].append(round_dist)\n",
        "      metrics['round_obj'].append(round_obj)\n",
        "      metrics['centers'].append(centers)\n",
        "  print(f'Average iteration duration = {(time.time() - start)/num_iters} sec')\n",
        "  return {k: jnp.array(v) for k, v in metrics.items()}\n",
        "\n",
        "\n",
        "def run_dist_kmeans_experiment(seed, num_levels_range):\n",
        "  experiment_metrics = {}\n",
        "  cache = {}\n",
        "  for num_levels in num_levels_range:\n",
        "    key = f'num_levels={num_levels} seed={seed}'\n",
        "    experiment_metrics[key] = {}\n",
        "    kmeans_metrics = experiment_metrics[key]\n",
        "    num_clients = 10\n",
        "    rng = jax.random.PRNGKey(seed)\n",
        "    rng, quant_rng = jax.random.split(rng)\n",
        "    quantizers = get_quantizers(\n",
        "        rng=quant_rng,\n",
        "        num_levels=num_levels,\n",
        "        num_clients=num_clients,\n",
        "        thresholds=jnp.array([0., 1.]),\n",
        "        update_thresholds=True)\n",
        "    for quantizer_name, quantizer in quantizers.items():\n",
        "      if not depends_on_num_levels(quantizer_name) and quantizer_name in cache:\n",
        "        # Does not depend on num_levels and already run so we can safely reuse.\n",
        "        kmeans_metrics[quantizer_name] = cache[quantizer_name]\n",
        "      else:\n",
        "        kmeans_metrics[quantizer_name] = run_dist_kmeans(\n",
        "            mnist_x,\n",
        "            quantizer_name,\n",
        "            quantizer,\n",
        "            rng,\n",
        "            num_clients=num_clients,\n",
        "            num_centers=10,\n",
        "            num_rounds=20,\n",
        "            num_iters=10,\n",
        "            verbose=False)\n",
        "        cache[quantizer_name] = kmeans_metrics[quantizer_name]\n",
        "      print(f'Finished {quantizer_name} {key}')\n",
        "  return experiment_metrics\n",
        "\n",
        "\n",
        "experiment_kmeans_metrics = run_dist_kmeans_experiment(\n",
        "    seed=47, num_levels_range=[2])"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 60,
      "metadata": {
        "colab": {
          "height": 408
        },
        "executionInfo": {
          "elapsed": 687,
          "status": "ok",
          "timestamp": 1658265621296,
          "user": {
            "displayName": "",
            "userId": ""
          },
          "user_tz": 240
        },
        "id": "JBin4NsAzsix",
        "outputId": "eb6c92bd-d06b-4843-a1c1-85d79da2f4f6"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "None 20.0 39.434612 0.118846945\n",
            "Independent 20.0 42.976254 0.23430422\n",
            "Independent+Rotation 20.0 68.85003 2.8117113\n",
            "TernGrad 20.0 42.072174 0.08683029\n",
            "DRIVE 20.0 41.645218 0.13038877\n",
            "Correlated 20.0 39.974834 0.12771523\n",
            "Correlated+Rotation 20.0 42.20631 0.17438135\n"
          ]
        },
        {
          "data": {
            "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYcAAAEOCAYAAABiodtuAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90\nbGliIHZlcnNpb24zLjMuNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8QVMy6AAAACXBIWXMAAAsT\nAAALEwEAmpwYAABvFklEQVR4nO2dd3gVVd7HP7/b0hNaCglC6BAQpIhioYmCYgEVXUAXUBCWF9uC\nsIIKiBXB1V3RtVFWwXUVdBVRBCRip4lKh0DoEAKEkHrbef+Ye2/uTQ+EJMD58MwzM6fNbw438z39\niFIKjUaj0Wj8MVW3ARqNRqOpeWhx0Gg0Gk0RtDhoNBqNpghaHDQajUZTBC0OGo1GoymCpboNqCzq\n1aunEhMTq9uM85Ls7GzCwsKq24zzFp1/Z4fOv7PnbPJw/fr16Uqp6MLuF4w4JCYmsm7duuo247wk\nOTmZHj16VLcZ5y06/84OnX9nz9nkoYjsLc5dNytpNBqNpghaHDQajUZTBC0OGo1GoylClYmDiJhF\nZLqI7BGRPM/5GRGx+IUREZkqIodEJFdEkkWkTVXZqNFoNBqDqqw5TAT+D3gIaAU87Ll/3C/MBGAc\n8CBwOZAGLBeRiCq0U6PRaC56qnK00lXA50qpzz33qSLyGXAFGLUG4BHgBaXUIo/bUAyBGAy8WYW2\namoQmZmZpKWl4XA4qtuUYomKimLr1q3VbcZ5i86/s6ekPLRarcTExBAZGVnhNKtSHL4HxohIK6XU\nNhFJAnoBz3v8GwNxwNfeCEqpXBFZjSEsWhwuQjIzMzl69CgJCQmEhIRglCFqFqdPnyYiQlduzxSd\nf2dPcXmolCI3N5eDBw8CVFggqlIcXgQigC0i4vI8+1ml1Ose/zjP+WiheEeBhOISFJEHgAcAYmNj\nSU5OrmybLwqysrJqbN5FRkbSsGFDXC4XWVlZ1W1OsbhcLk6fPl3dZpy36Pw7e0rLw9q1a7Nr1y4y\nMzMrlGZVisPdwJ8xmog2A5cBr4rIHqXUu37hCm8wIcW4GQGVegt4C6Bz586qOibSDP9qOABz+86t\n8mdXFjV5EtLWrVupW7dujawxeNEl37ND59/ZU1oehoeHc+zYMTp27FihNKtSHF4CZiql/uO5/0NE\nGmF0SL8LHPG4xwH7/eLFULQ2obmIqMnCoNHUdM7076cqRyuFAq5Cbi4/G/ZgCMT1Xk8RCQauBX6s\nCgM1FzZ3v/kTd7/5U3WbodGcF1RlzeFz4G8isgejWakD8Ffg3wBKKSUirwCTRWQbsAN4AsgCFlah\nnRqNRnPRU5Xi8CAwHXgdo6noMPA28LRfmBlACDAbqA38AtyglNK9VRqNRlOFVFmzklLqtFLqEaVU\nI6VUiFKqiVJqklIqzy+MUkpNVUrVV0oFK6W6K6U2VZWNGk1lMWzYMESEZ555JsA9OTkZESE9Pb2a\nLNNoyodeW0mjOUcEBwczY8YMjh07Vt2maDQVRouDRnOO6NmzJ4mJiUyfPr3EMKtXr+aKK64gODiY\n2NhYHn30Uex2u8+/R48ejBkzhkmTJlGvXj1iYmIYP348brfbF8ZutzNx4kQaNGhAWFgYl19+OcuW\nLTun76a58LlgNvs5X7kQ5klUNdM+38yWQxWb0AOw5bAR50xGLCXFRzLlloqtAWkymXjhhRfo378/\nDz/8ME2bNg3wP3jwIDfeeCP33nsv8+bNIyUlhREjRmAymZg1a5Yv3IIFC3j44Yf58ccf2bhxI4MH\nD6ZTp04MGjQIgOHDh5OSksLChQtp0KABS5cu5ZZbbmHt2rW0b9++wu+q0YCuOWg055SbbrqJq6++\nmsmTJxfxe/3116lfvz6vv/46rVu35uabb+aFF17gtddeIycnxxcuKSmJp59+mhYtWnDXXXfRs2dP\nVq5cCUBKSgoffPAB//3vf+nWrRtNmjRh7Nix3HTTTbz5pl5xRnPm6JqD5ryjoiV4L94aw4ejulam\nOWUyY8YMrrzySsaPHx/gvnXrVrp27YrJVFBGu+aaa7Db7ezatYt27doB+M5e4uPjSUtLA2DDhg0o\npUhKSgoIk5+fT69evc7F62guErQ4aDTnmMsvv5w77riDiRMn8uSTT/rclVIlzl71d7darUX8vH0O\nbrcbEWHt2rVFwoWEhFTWK2guQrQ4aDRVwHPPPUdSUhJfffWVzy0pKYn//ve/uN1uX+3h+++/x2az\nFemfKIkOHTqglOLIkSP07NnznNiuuTjRfQ4aTRXQrFkzHnjgAV599VWf25gxYzh06BBjxoxh69at\nfPHFF/ztb39j7NixhIaGlivdFi1aMGTIEIYNG8bHH3/M7t27WbduHTNnzmTx4sXn6nU0FwFaHDSa\nKuKpp57CYimorCckJPDll1/y66+/ctlll3HfffcxaNAgnnvuuQqlO3fuXIYPH86ECRNo1aoVN998\nM6tXr6ZRo0aV/QqaiwjdrKTRnAPmzZtXxC0mJqbImvvdunXjl19+KTGd4vbZKJy21Wpl6tSpTJ06\n9Qws1WiKR4uD5qKhqkcpaTTnM7pZSaPRaDRF0OKg0Wg0miJocdBoNBpNEbQ4aDQajaYIWhw0Go1G\nUwQtDhqNRqMpghYHzcXD3H7GodFoykSLg0aj0WiKoMVBo6mh3HzzzQwbNqy6zSg3bdu21bO0LyC0\nOFQCSqnqNkFTwxg2bBg333xzdZtx3jN16lTatm1b3WZclOjlM84Ch8vB7lO7OZl3kpsW30TDyIYk\nRibSMKIhjSIb0TCyIfFh8ZhN5uo2VaPRaCqErjmcIbnOXB5c9SAn8k5QJ7gObeq24UTuCT7Z+QnP\nr3me0StGc9Pim7h8weXc+umtPLjyQV5a+xL/3f5ffjn8C0eyj+BW7rIfpDnv8dYiXn31VRISEqhd\nuzbDhw8P2Ao0JyeHYcOGER4eTmxsbLErs9rtdiZOnEiDBg0ICwvj8ssvZ9myZT7/5ORkRIQlS5Zw\n2WWXERwcTKdOnVi/fn1AOj/++CPdu3cnNDSUhIQE/vKXv5CZWbAnd48ePRgzZgyTJk2iXr16xMTE\nMH78eN8GQwBpaWncdttthISE0KhRI+bMmVPE3lOnTvHAAw8QExNDREQE3bt3Z926dT7/efPmER4e\nTnJyMm3btiUsLIyePXuyZ88en/+0adPYvHkzIoKIFLugoebccNHXHIZ/NRyAuX3nljvOaftpxq4c\ny69pv9IoohHRodG81P0lwGhiOp53nL2Ze33Hvsx97D29l58O/0S+K9+XTrA5GBEh0haJW7kxidbq\ncvHl3+DIHxWPd+R343wmI5biLoUbX6h4PA/fffcd9evXZ8WKFezfv5+77rqLFi1a8PjjjwMwfvx4\nli9fzqJFi0hISGDatGmsXr2a22+/3ZfG8OHDSUlJYeHChTRo0IClS5dyyy23sHbtWtq3b+8LN378\neJ8QTZs2jX79+rF7925CQ0P5448/uOGGG5g2bRrvvPMOJ06c4JFHHuG+++7j448/9qWxYMECHn74\nYX788Uc2btzI4MGD6dSpE4MGDQIMwdu7dy8rVqwgNDSURx99lNTUVF98pRT9+vUjKiqKJUuWUKdO\nHebPn0+vXr3Yvn079evXB4ztTF9++WXmzJlDcHAwQ4cOZfTo0Sxbtoy7776bTZs2sWTJEt/qtFFR\nUWf8f6CpGBe9OFSUk3knGbV8FDtP7mRGtxl8uP3DAH8RoV5IPeqF1KNTbKcAP7dyk5aTFiAai3ct\n5mjOUT7Y9gFDWg+pylfRVCGRkZG88cYbWCwWWrduzcCBA1m5ciWPP/44WVlZvPvuu8yZM4c+ffoA\nxh4NDRo08MVPSUnhgw8+IDU1lYYNGwIwduxYVqxYwZtvvsnrr7/uC/vkk08WSWfhwoWMGDGCl156\nibvvvptx48b5wr/xxht06NCBtLQ0YmJiAGOXuqeffhowNhR6++23WblyJYMGDWLHjh18+eWXfP/9\n91x99dUAzJ8/nyZNmvjSXLVqFRs3buTYsWO+7UqnT5/O559/znvvvceECRMAcDqdzJo1i44dOwKG\nsA0fPhy3201ISAjh4eFYLBbi4uIq8X9DUx60OFSAo9lHeWD5AxzMOsirvV6lW4NuRcShNExiIi4s\njriwOK6ofwUAm9I3sStjF6+sf4Wr468mMSrxHFl/AXGmJXhvjWH4F5VnSzlJSkoK2OgnPj7et49D\nSkoKdrudrl0LlhQPDw/n0ksv9d1v2LABpRRJSUkB6ebn59OrV68At+LS2bJlCwDr169n165dfPhh\nwe/WO6AiJSXFJw7t2rULSDM+Pp60tDQAtm7dislkokuXLj7/Ro0aER8f77tfv349OTk5REdHB6ST\nl5dHSkqK7z4oKIjmzZsHPMfhcJCRkUGdOnXQVB9aHMrJ/tP7Gfn1SE7mneSN3m9wedzllZKuiNAo\nshGpmalM/mEy/+77b92BfQFitVoD7kXE14ZfntFubrcbEWHt2rVF0vKWzMuD2+1mxIgRPProo0X8\nEhISKtXe2NhYvvvuuyJ+kZGRvmt/wfQ+xxtfU71ocSgHu07u4oHlD2B323m3z7u0rVe5Q+tsZhuT\nr5jMxO8mMm/zPO6/9P5KTb8mcyZ9PhcazZo1w2q18vPPP/uaZrKzs9m0aRNNmzYFoEOHDiilOHLk\nCD179iw1veLS+fOf/wxAx44d2bx5M82aNTtje1u3bo3b7Wbt2rVcddVVAOzbt49Dhw75wnTs2JGj\nR49iMpkCmpsqis1mw+VynXF8zZmje0DLYFP6JoYtGwbA3D5zK10YvNzY+Eaub3Q9szfOZsfJHefk\nGZqaSXh4OPfffz8TJ05k+fLlbN68mfvuuy/go9iiRQuGDBnCsGHD+Pjjj9m9ezfr1q1j5syZLF68\nOCC9Z555JiAdm83G4MGDAZg4cSJr1qxh9OjR/Prrr+zatYslS5YwatSoctvbsmVL+vbty6hRo/jp\np5/YuHEjw4YNC6jB9O7dm6uvvprbbruNL7/8kj179vDTTz8xZcqUYmsTJZGYmMjevXvZsGED6enp\n5Ofnlx1JUylocSiFtUfWMuLrEYRbw5nfdz7NazcvO9IZIiI8ceUTRNgieOL7J3C4HOfsWZqax8yZ\nM+nZsycDBgygZ8+etG3blm7dugWEmTt3LsOHD2fChAm0atWKm2++mdWrV9OoUaOAcC+88ALjxo2j\nY8eO7Ny5kyVLlhAWFgYYfQmrV68mNTWV7t270759ex5//HFiY2MrZO+8efNo3LgxvXr14pZbbmHw\n4MEkJib6/EWEpUuX0qtXL0aOHEnLli2566672L59e0DfRFnccccd3HTTTVx33XVER0fzwQcfVMhO\nzVmglLogjk6dOqkzYdiXw9SwL4cVcf92/7eq03ud1K2f3KqOZB2pcPwzff6KvStU23lt1Wu/vnbG\naVb0+f0/6F8lzyrp+aXl35YtWyrvYXNuMo5KJjMzs9LTPBNWrVqlAHXs2LHqNqVC1JT8O58pKw9L\n+zsC1qlivqm65lAMX+35ioe/eZgmUU2Y23cusWEVK1WdDdc1vI5bm97K27+/zeb0zVX23IuC4V9U\ny0glTc1lz6k97Dm1p7rNqJFocSjEoh2LmLB6Au2i2/Fun3epE1z1w+kmdplI3ZC6TPp+UsCkOY1G\no6kqtDj4MX/zfKb+NJWrEq7iX9f/iwhbRLXYEWmLZPpV09l9ajev/fpatdigOf/o0aMHSinq1atX\n3aZoLgC0OGD0u7y+8XVmrpvJ9Y2u5589/0mIpfxjx88FVyVcxV0t7mL+5vlsOLqhxHDDvxruGw6q\n0WguPvac2sNRx9FKT/eiFwelFPuz9vPGb2/Qv1l/ZnSbgdVsLTtiFTCu8zjiw+OZ/P1kchw5ZUfQ\naDRVyoXcZ3HRi8P+rP2k5aRxT+t7mHbVNCymmjMvMNQayjNXP8PBrIO8vP7l6janWHTNRaO5MLno\nxaF2UG3iw+KZcPmEGrkqaue4ztybdC8fbv+QHw/+WN3maGoYZ1tyvZBLvpqzo8q+hiKSKiKqmOML\nj7+IyFQROSQiuSKSLCJtzrVdEbYI4sPjfWu61EQe6vgQTaKa8NSPT5Fpzyw7gqZYdC1Hoyk/VVlU\nvhyo73d0BBTwX4//BGAc8KAnbBqwXESqZ8hQDSLIHMSz1zxLem46L655sbrNqVHokm/1Uhk1l3PR\nmXo+UVN/w1UmDkqpY0qpI94DuAnIBD4So9j+CPCCUmqRUmoTMBSIAAZXlY01mbb12jLi0hF8lvIZ\n3+z7prrN0VQBN998M8OGDatuM8pN27ZtmTp1anWbUW7cyo3D7SDXmcuR7CNVvmRNYmIiM2fOrNJn\nVoRqaWT3iMH9wPtKqRygMRAHfO0No5TKBVYDV1WHjTWRUe1G0apOK6b9NI2TeSer25yzwul2krw/\nmZ0nd7IpfROf7Pzkgto21bs1aFnU1FJjTWHq1Km0bXt2i12mpqb6thkVEaKiouhyRRfe/s/bOFwO\nBOF47nF2nNzBgdMHShwZWN7/0/K+w9q1axkzZkyF06sqqmtozvUYgvCO5967zVPh+uVRIIESEJEH\ngAcAYmNjfVsJVoSMjAyAM4pbHfEHBA1gxokZPPT5Q9xX775Keb7L5aoy+084T/BT1k/8nPUzGa4M\nzJgxi5mnfnyKt9a9xe21b6d5cMECh1FRUZw+fbrE9Lwrl5YW5kzCVgSXy1UkTYfDgdPpLPNZpdnk\ndDpxOByV9v7nOr7b7SY/P79CablcLpRSJcbJz8/H7XaXmWbbtm154403uPbaa4v4ZWVlAbB48WKS\n2iZx4MQB5rwzhweHPsjibxbTKqkVda11Oe06TWZ+JqfyTxFkCiLCHEGIhPj6I4v7Py1P/pX0DsHB\nwbhcrkr5PygtD8HYZKnCf+PFLbh0rg/gI2CN3/1VGP0PlxQKNxf4qjxpVvbCezU5/tu/v63azmur\nlu5eWinPP5uF98rzfIfLob7Z+40as2KMaje/nbp03qVq1PJRakXqCvXnpX9WQ5cOVUtSlqjeH/VW\nbee1VY9884jad2qfUqrshfd2Z+xWuzN2V5qtZ0Jxi54NHTpU9evXL+D6lVdeUfHx8apWrVpq2LBh\nKjs722d/dna2Gjp0qAoLC1MxMTHq2WefVf369VNDhw71pZmfn68mTJigEhISVGhoqOrcubOat2ie\n7/29C+99/vnnqn379iooKEh17NhRrVu3LsC2H374QXXr1k2FhISo2PqxavB9g9WpU6d8/t27d1d/\n+ctf1OOPP67q1q2roqOj1bhx45TL5fKFOXr0qLr11ltVUHCQim8Qr959913Vpk0bNWXKFF+YjIwM\nNXLkSBUdHa3Cw8NVt27d1Nq1a33+c+fOVaFhoWr+4vmqTZs2KjQ0VPXo0UPt3r3b5+/5LviOuXPn\nBryLN/8aNWqkVq1aVez/z549exSgvvn+G7X1+Fa16dgmtfPwTgWoKS9O8eXf77//rnpd10sFBwer\nqFpR6ra7b1PrUtep9Jx09eRTTxaxZdWqVWp3xm416pFRqkWLFio4OFg1atRIPfbYYyo3N7fMd2jU\nqJF66aWXfO+wd+9e1b9/fxUeHq7Cw8PVgAED1P79+33vMWXKFNWmTRv1wQcfqCZNmqjw8HB12223\nqXUp69TO4zuLfXcvZ7LwXpXXHEQkBrgN+D8/5yOecxyw3889hqK1iYueYW2GsWr/Kp75+RkSIxOx\nmW3VbVKxHMo6xOKdi/lk5yek5aYRHRLNiEtHcHvz20kINyqE7299H4B+TfrRq2Ev/r3537y76V2+\nPfAtQ1oPoW9o3yLpvrjmRbad2AZAnjMPgGBLcJn2eOOcyYilVnVaMbHLxArH8/Ldd99Rv359VqxY\nwf79+7nrrrto0aIFfxrzJ8DYO3n58uUsWrSIhIQEpk2bxurVq7n99tt9aQwfPpyUlBQWLlxIgwYN\nWLp0KSP/NJJPv/mUxtc09oUbP348r776qi+dfv36sXv3bkJDQ/njjz+44YYbmDZtGu+88w6b9m1i\n+uPTue+++/j44499aSxYsICHH36YH3/8kY0bNzJ48GA6derEoEGDAKOJZe/evbz36XuEhIQw86mZ\npKam+uIrpejXrx9RUVEsWbKEOnXqMH/+fHr16sX27dupX78+APZ8O2++8iZz5swhODiYoUOHMnr0\naJYtW8bdd9/Npk2bWLJkia/UGxUVVeG8z3ca65Ol5abRyNyI6JBo3n7jbQAsVuMTmJOTQ9++fbn8\n8stZs2YNx48fZ8TIEUx+cDKz5szi1vtu5bdNv5F1KosF7y8AoE6dOhzMPUhoaChz5swhISGBLVu2\nMHr0aIKCgpg+fXq530EpRf/+/QkODuabb75BRBg7diz9+/dn7dq1vtpLamoqH374IZ988gnZ2dn8\n6U9/Ytb0WTw96+kK50tZVEez0jAgH/iPn9seDIG4HlgLICLBwLXAY1VsX43HYrLw7NXPMvDzgezN\n3EuzWme+q1dl43Q7WX1gNR/t+IgfDv4AwNUJVzOpxSS6NeiG1VTy7PMQSwij2o9iQPMB/PPXfzJ/\n83w6tulI/dz61A6uXaOHG5dFZGQkb7zxBhaLhdatWzNw4EBWrlzJn8b8ieysbN59913mzJlDnz59\nAGPvhgYNGvjip6Sk8MEHH5CamkrDhg0BGDt2LJ99+RkL5y3kpmtu8oV98skni6SzcOFCRowYwUsv\nvcTdd9/NuHHjALDEWJg+azo3d7uZtLQ03x7SSUlJPP208cFp0aIFb7/9NitXrmTQoEHs2LGDL7/8\nku+//574tsbeDPPnzw/Y8W3VqlVs3LiRY8eO+TYBmj59Op9//jnvvfceEyZMAIymsykzpvj2ox4/\nfjzDhw/H7XYTEhJCeHg4FouFuLg4KopbuTmWe4x9p/cBMPzW4ZhMJnJzc3G73TRu3Jh+A4x9xRcs\nWEBWVhbvvfceERHGAMl33n6Hnj178tKxl4hrEIdYBafJiSPMQd3gutisNsiFByc8SOMoQ5wTExOZ\nNGkSM2fOZPr06eV+h++Tv+e3334jJSXFty/GwoULadasGStXrqR3796+/Jo3b55PYB544AHefvft\nCudNeahScfB0RI8A/qOU8jWQKaWUiLwCTBaRbcAO4AkgC1hYlTZWNWe6PWZiVCKPdHqEF9a8wNGc\no/x+7HdynbnkOHKMs9M4l+WWkpGCy+3inqX3UDu4NnWC61A7qHbBdbDnOsi4LqmEfjDrIIt3LubT\nnZ/6agkj243kjuZ3EB9e/s1dAGJCY5h+9XQGtRrE8b3HOZx9mBP5J4gLjSPcFh5Qgvd25nr/OEuj\nuC1JKxK/OPac2oPL5SKC0kdcJyUlBeyXHB8fzy+//ALAvj37sNvtdO3a1ecfHh7OpZde6rvfsGED\nSimSkpIC0s3Pz6drt64BbsWls2XLFgDWr1/Prl27+PDDDwFQ+JpwSUlJ8YlDu3btAtKMj48nLS0N\ngK1bt2IymejSpQv7c/ajlKJRw0YBm/isX7+enJwcoqOjA9LJy8sjJSXFd28LstGkeYGoxMfH43A4\nyMjIoE6dkldEvvHGG/nuu+9QGLbn5uRy4403YjYX7L++Yf8GHC4H4dZwwPjYtmnThh07dvDoo4/y\n1ltvUat2Ld87tWvXzicMAFdddRUmk4k9O/dwaetLiQyKJNucTZY9i8z8TEKsIbjdbpZ9vowP3vqA\nXbt2kZWVFdCPUF5StqcQHx/vEwalFI0bNyY+Pp5NmzfRo1cPXG4XDRs1JDg8mDxnHgpF3di6pKen\nl2tf74pS1TWHHkBz4J5i/GYAIcBsoDbwC3CDv4hoAhnUahCzf53NgawDDFk6pMRwFpOFUEsoIZYQ\nQiwhhFqN69rBtQmxhOBwOAg2B3Mw6yCb0jeRkZeBUzmLTSvEEuITkFrBtdhzag8Ot4MbF90IwDUJ\n1zC5xWS6Neh21kuRJNVNYmvaVhIiEjiac5S9mXsJt4UTFxpHkCXorNKuaqzWwBqTiOB2G6OzyvOH\n7Xa7ERHWrl2L1WpFKYXD5eBQ9iFsQTaO5x73jbIpLT23282IESN49NFHAdh/2mjFvSTiEhISCsZ+\nFGev0+XkVP4pTuYaI+W2n9iOmI3a3LYT23ApF9mObHIdubhcLmJjY4vdEjQyMtJ37S+Y3ud47SyN\nd955h9zcXJ/9995yLy+++CIdO3ckPTedLHsWgtAoshHpWekANGjQgObNm9O8eXPCw8MZOHAgX/78\nJXXq1kEpVWLN1OtuEhNB5iBa1G5BRn4Gx/OOs+6XdTx838OMnTiWvz79VyKjIlm5dCUvPPUCO0/u\nBOB47nHsLjs7T+70iRnKqGUfyzlGrjMXu8uOS7nYenxrgGA73U7SctLYfmI7x/OOo0yKXSd3+Ww7\nnnsct8tdkG4lUqXioJRaBRT7P+DpGJnqOTTlwCQmmtVqRqY9k792/qshANaQQCGwhJa6kODwr4aT\nkZHBO33e8bkppci0Z3Iy7yQZ+RmcyDvBybyTnMw/WXCdd5Ljucc5bT8NAg+0e4Dbm99e4VpCeYgM\niiTcFs6JvBMcyznGroxd1AmuQ3RodNmRzwMaNWmE1Wrl559/9jXNZGdns2nTJpo2bQpA+8vao5Ri\n+97tdLyqIznOHJRbUb+e0XZ/JPsIR3OM7rlFKxYxoM4AbGYbzjwnf2z6g7sG34XdZadjx45s3ryZ\nZs2MpkjzKaOk7V9zUiicbicnck+Q58ojz5lHRn4G2Y5sDpw+QHRiNG63m5Q/UmjdqTUmTJw8epKj\nh4+S7chm96ndRDeP5ujRo5yyn6JNizYEmYPOqFnQZrMVWwr3CpnXfovFQni9cKgHdalLy5CW1Aup\nh0lMpJNeJH737t1JSkriny/+kykzppCUlMScOXM4ffq0r/bw448/4na7ad26dYAtZpOZuiF1qRNc\nh7lr5xJTP4a/TvwrAIKw4KDRJ+GtZYcGh6LcKmClZ8EYVmsz2zCLmeYtm3P08FGyjmbRsFFDRIS9\ne/aSdiSNzu07ExcWR4QtAovJQkJEghEfoW5IXWOIbvGf1bOi5qwyd55yps1ClYXZZKZ2cG26NehW\nduByIiJEBUURFVR255+3qWZsh7GV9vziMImJeiH1qBVUi7ScNE7knSAjPwOzyVxqP8b5QFh4GPff\nfz8TJ04kOjqa+Ph4pk2bhsvlIs+ZR+qpVFQ9Rb87+zFm5Bj+Nv1vdO7YmbzTeSz/ZjkNExsyYvAI\n9oUZbevv/P0dEuISqBNTh3+8+A/MVjNX3nQlO0/u5M5RdzK472Duue8eho4YigQJu3fu5vvl3/Ps\nK8+S58wjx5FDZn4mh7MPYzaZCTYHE2QOItgSTNNaTWl9RWv69u3LE48+wdSXpxIcHMysKbMICQmh\nbkhdGkQ0oO8Nfel4RUcG3TmIvz71V5q1bEb28Wx+WPUDfa/vS68evcqVN4mJiezdu5cNGzbQsGFD\nIiIiCAoKrDW6ldsQs7wThFhCqB9enyBz2TXLcePGcefAOxn50EiGDBnClClT+POf/8zTTz/NyZMn\nGTVqFLfffrtPSBMTE/nyyy/Zvn07devWJSoqiqbNm5J2OI3vPv+Orl27smzZMpYsWgIYtTGA9i3b\n88/9/yRtZ1rAO5jFTFRQFDazjR7X9aB9+/Y8MvIR/vGPf6CU4qEHH6Jjx44MuHEAIkKIJQSzmKkV\nVMv3Dl4BOhf9cTVvpTmNphQsJgvx4fE0rdWUUGsoDpcxw/VQ1iFO5Z/C6S6+Oaym8/yM57mm2zX0\nH9Cfbj26Eds0lg5XdiDPmYdLuagVVIt5c+dx//D7eeXpV+jRuQeD7xjM2h/XktAwAavZ6iuZzpwx\nkxeffJH+PfpzdN9Rlny2hKT4JOqH16drp658tPQj9u3bx4A+A7i1263MfHomYbXDyHPmEWwJxma2\nEW4Lp0XtFrSs3ZLEqERCLCFYTVaCLcGYxMS8efNo3LgxQ24dwshBIxk8eDCJiYm+D15CRAKrvl7F\nDb1v4OlxT3PTFTcxeuho/tjyB/lh+ew4ucM3kVMpRb4z3+gHc+QCkJGXQXpuOtfeeC29buhFr+t6\nER0dzT/f/ScpGSnsPLmTbSe2kePI8bW/R4dE0yiyUbmEAYwZ6I0TGzP/1fmEhoaybNkyMjMz6dKl\nC7fddhtdu3Zlzpw5vvAjR46kdevWdO7cmejoaH744Qeuu/E6Rj40kkceeYR27dqxfPlyX0e+lzvu\nuIObbrqJ6667jujoaD744IMitogIn376KdHR0fTo0YOePXsSFxfHp59+Wm0DMeRcdGRUB507d1br\n1q2rbjOqnOI6WSsaPyMjg0/+9Em1Pb+0+Fu3bvVV64tjV8YunC4nCuWbYR1kCSLMGkaYJYxQa6iv\n76MyO6SVUjjcDvad3ofL5aJuaF1jfLj3n1IBbcfee7dyB/h7P2zecN4SYqgllDBrmFFaNJlLtMPf\n/uTkZHr27MmxY8fKtRucUoo9p/agUCRGJpb6nPI8vzzPs7vsZDmyyHHkkO3IxqXK7rg1iQmTmDCb\nzMZZjImTJjGRZc8CgaZRTc/I/rPlbAc0VEYa3kERzeqUPGqxtL8jEVmvlOpc2F03K2nOa8xixmwx\nkxiZSK4zl2xHNtmObE7mneSEOgEYVe8waxj/6PUPQi2hFUrfKwL5rnzjcOb7rv2X+ziaHTgdx9sO\nXObZc10vtB6hllBfybwq8C0pgVTJh1VECLIEEWQJom6IIaa7T+3G6XISGx7r++AXPpdWcvZ+WKtD\nGC50tDhoLghEhFBrKKHWUKKJxq3cAWJxIu8Ex3OPIwjB1mDCLGGEWcN88b0ikOfMKxACz+Ffu7aY\nLASZg6gVXIsgcxAn807idrlpUrtJwIe/vHg/bvVCLr59n0XEJwL+7eiamoEWB80FiUlMRtOSRwDc\nyu1rysh2ZpOem056rjGKRUTYemJrEREItgQTag01OmPNRlt84eG5p/JPoUTViJJrjx49zsl4d83F\niRYHzUWBSUyE28IJtxkTolxuFznOHA5lHUIp5asJeI+a8LHXaKoTLQ6aixKzyUyELcK3LlVcWMWX\nZ9CcXUespmajxUFTrVT3PBGNprqpqQKrxUGjOQsaRzWu9P0hNJqagBYHzUXD3nv/DECj9/5dzZZo\nKouaWuq+ENDioNGcx+iPo+ZcocXhPKe62+yr+/ma6ufjjz9m4MCBF+Uw2pogzueqaVOvraTRVDL+\nm9kXdwwbNuycPdvhcDBr1iw6depEeHg4kZGRtGvXjr/97W/s37+/7AQ0Gg8VqjmIyKXAKKApcJ9S\n6rCI9Af2KqV+PQf2aTSlUhNKboU5fPiw73rJkiWMHDkywM27MxqUz3673Y7NVvZWsHa7nT59+rBx\n40amTJnCNddcQ61atUhJSeGLL75g1qxZvPLKK2f1DM1FRHEbSxd3ADcAecAnGNt8NvG4jwM+LW86\n5+ro1KlTiRtoa0qnpI3ZawKlbYxeUVLvuVel3nNvpaXnJTMzs0S/jz76SBl/ZgV89tlnqmPHjioo\nKEglJiaqSZMmqfz8fJ9/o0aN1JQpU9Tw4cNVVFSUuvPOO9XcuXNVWFiYWrFihWrTpo0KDQ1VPXr0\nULt37/bFe+GFF5TJZFIbNmwo1ha32+277t69uxo9erQaN26cqlevnurcubNSSqlZs2apSy+9VIWG\nhqr4+Hh1//33q5MnTwakM3/+fNWwYUMVEhKi+vXrp1577bUi71gRSss/TfkoKw9L+zsC1qlivqkV\nqTlMB/6qlHpdRPwbuJI9AqHRVAlHnnuO/K3bKhwvb5sRxztqqSIEtW5F3KRJFY5XmGXLljFkyBBe\nffVVunXrxr59+xg9ejT5+fnMnDnTF+7ll1/miSeeYN26dSil+OGHH8jPz+f5559nzpw5BAcHM3To\nUEaPHs2yZcsAYxvM66+/ng4dOhT77MJrPr3//vs88MADxnabnv4Ck8nEK6+8QpMmTdi7dy8PPvgg\nDz74IO+99x4Av/zyC8OGDWP69OkMHDiQVatWMakS8kVT86iIOLQBlhbjfgIoebNXjUbj49lnn+Wx\nxx5j+HBj+fCmTZvy4osvcs899/DSSy/5PuDdu3dnwoQJvng//PADTqeT2bNn07JlSwDGjx/P8OHD\ncbvdmEwmduzYQY8ePQKeN2jQID7//HMAGjVqxObNm31+jRs3ZtasWQHhH3nkEd91YmIiM2bM4Lbb\nbmP+/PmYTCZeffVVrrvuOiZPngxAixYtWLt2Le+++27lZJCmxlARcTgJJACphdw7AgcqyyCNpizO\ntARfE+Y5rF+/njVr1vDiiy/63NxuN7m5uRw5coT69Y1tPzt3LrK8PkFBQT5hAIiPj8fhcJCRkUGd\nOsWXz/7+978zffp03n333SKbzHTq1KlI+G+++Ybnn3+erVu3curUKVwuF3a7nSNHjhAfH8/WrVu5\n5ZZbAuJ07dpVi8MFSEXEYSHwkojcBSjAIiLdgZmAHs+o0ZQDt9vNlClTGDhwYBG/6OiCPbHDwsKK\n+FssgX+u3lqG223sK9GiRQu2bQtsbouLM9aMqlu3bpH0Cj9j79699OvXj5EjR/L0009Tt25dNmzY\nwKBBg7Db7QAX5XDVi5WKiMMTwDxgLyDAFs95IfBspVum0VyAdOzYkW3btvn2Ja5MBg0axKRJk1i3\nbl2xNY+yWLduHXa7nb///e+YzcaqtEuWLAkIk5SUxM8//xzgVvhec2FQbnFQSjmAISLyFNABY47E\nr0qpnefKOI3mQuOpp57i5ptvplGjRtx1111YLBY2bdrEmjVrmDFjxlml/eijj7J06VJ69+7NlClT\nuPbaa6lbty67du3if//7n++DXxLNmzfH7XbzyiuvcPvtt/Pzzz8XGfr60EMPcdVVV/H8889z5513\nkpyczCefnNkWs5qaTbknwYnIbSJiUUqlKKU+Vkr9VwuDRlMx+vTpwxdffMGqVavo0qULXbp04YUX\nXqBhw4ZnnXZQUBArVqxg8uTJ/Pvf/6Zbt260atWKsWPH0qFDB5YvX15q/Hbt2vHqq6/y8ssvk5SU\nxDvvvBMwggrgyiuv5N133+WNN96gXbt2LF68mKlTp5617Zqah5S3DVFEcoAc4CPgPaXUj+fSsIrS\nuXNntW7duuo247wkOTm5yCiXmkJpG6NXlHPVIX369GkiIiIqNc2LCZ1/Z09ZeVja35GIrFdKFWmH\nrEifQyxwJzAYWC0i+4AFwPtKqe0VSEejqRb0aqwaTfkpd7OSUuq0UmquUup64BLgNeBGYIuIrDlX\nBmo0Go2m6jmjVVmVsabSaxgjl54Aig6Y1mg0Gs15S4VXZRWRniLyDnAUeAf4Fehd2YZpNBqNpvoo\nd81BRF4C/gTEAMswVmf9n1Iq/xzZptFoNJpqoiLNSlcDzwP/UUqdOEf2aDQajaYGUJFJcFedS0M0\nGo1GU3MoVRxE5Hbgc6WUw3NdIkqpxZVqmUZTyXwyawMAA8Z1rGZLNJqaT1k1h4+BOCDNc10SCih9\nbr5Go9FozhtKFQellKm4a41Go9Fc2FRkbaVuIlJETETELCLdKtcsjeb8ZtiwYYgIIoLVaiUmJoae\nPXsye/ZsHA6HL1yPHj184Ww2G02bNuXxxx8nPz9wEKCI8PHHH2O324mOjuaZZ54p9rmvv/46oaGh\nnDp1iuTkZF/ahY/CS3trNIWpSG1gFcXv+FbL46fRaPzo3bs3hw8fJjU1la+//ppbbrnFt1pqdna2\nL9zw4cM5fPgwu3btYsaMGcyePbvExexsNhv33HMPc+fOLXZvhTlz5nDnnXcSFRXlc9u8eTOHDx8O\nOJo3b17p76u5sKiIOAhG30Jh6gLZxbhrNBc1QUFBxMXFkZCQwGWXXcZf//pXkpOT2bBhQ8Dy3KGh\nocTFxdGwYUPuuOMOrr/+er7++usS0x0xYgS7d+8mOTk5wP23335j/fr1jBgxIsA9JiaGuLi4gKOs\n5bs1mjKHsorIZ55LBbwvIv71XTPQFqhRK7RqLmy+++8O0vdnVThe+oHTQMGopYpQ75Jwrr2rRYXj\nFaZt27b07duXRYsWMW3atCL+v/32Gz/88AOJiYklptGmTRuuuOIK5syZQ8+ePX3u7777Ls2bN6db\nN93Kqzl7ylNzOO45BGMf6eN+xwHgX8A95XmYiNQXkfkickxE8kRki2erUa+/iMhUETkkIrkikiwi\nbSr6UhpNTSYpKYndu3f77t966y3Cw8MJCgrisssu49ixYzz22GOlpjFixAgWLVrEqVOnAMjPz2fB\nggXcf//9RcImJiYSHh7uOxo0aFC5L6S5ICmz5qCUGg4gIqnAS0qpnDN5kIjUAn4Avgf6AceAJhjD\nZL1MAMYBw4DtwFPAchFpqZQ6fSbP1Vx4nGkJvqbMc1BK+fZ/Brj77ruZMmUKmZmZvPjii9SuXZs7\n7rij1DT+9Kc/8eijj/LBBx8wevRoPv30UzIzMxk6dGiRsKtWraJ27dq+e92kpCkPFVk+42OgGfC7\nv6OItAOcSqktZcSfABxWSv3Zz22PXzoCPAK8oJRa5HEbiiEeg4E3K2CrRlNj2bJlC02aNPHdR0VF\n+faUfv/992nTpg3z5s1j2LBhJaYRHh7OXXfdxZw5cxg9ejTvvvsu/fr1Iy4urkjYxo0bU69evUp/\nD82FTUU6pN/C6F8oTJLHryz6A7+IyIcikiYiG0VkrBQUoRpjTLjz9cQppXKB1YBeukNzQbBp0ya+\n+uor7rzzzmL9rVYrkyZN4vHHHycnp/RK+ogRI1i7di1Llixh5cqVRTqiNZqzoSI1h3ZAcZv6rAUu\nLUf8JsAY4O/AC8BlwD89fq9hCAMYS4H7cxRIKC5BEXkAeAAgNja2yOgNTfnIysqqsXkXFRXF6dOV\n06LocrkAKi09/3QLp+lwOMjJyWHXrl243W7S09P59ttvmTlzJpdddhmjRo3i9OnTuFwu7HZ7QPxb\nbrmFxx9/nFmzZvHII4/43HNzcwPCtW3bllatWnHvvfcSGxvLNddcE+DvFZc9e/aQkZERYF/t2rWx\n2WyVmAtnTnH5p6kYZeVhXl5ehf/GKyIOLiCqGPfaGJ3VZWEC1imlHvfc/yoizYH/wxAHL4WHy5Y0\nhBal1Ft4ai2dO3dWNXUf5JpOTd9DurL2F/a2tVf2fsXF7d9rtVpZtWoVzZs3x2w2U6tWLdq2bcvU\nqVMZNWqU78NsNpux2WxF4j/44IO8/PLLPPLIIz6/kJCQIuFGjhzJuHHjGDNmDLVq1QrwCw0NBaBL\nly5FbF6+fDm9e9eMbVj0HtJnT1l5GBwcTIcOHSqUphQ3kabYgCL/wxCIgUopl8fNAnwEWJVSN5cR\nfy+wXCk1ws/tXuBfSqkwEWkCpABdlFJr/cJ8AaQrpYr2tPnRuXNntW7dunK9iyaQmi4OJW2MXlHO\nVYe0/ridHTr/zp6y8rC0vyMRWa+U6lzYvSI1hwkYI412icj3HrdrgHCgPAOrfwBaFnJrgbHVKBid\n00eA6zGaqhCRYOBaoPRxfRpNOajuUUoazflEuTuklVLbMfodFmIso1EXWAC0V0ptLUcSfweuFJHJ\nItJMRAYCDwGzPekr4BXgbyJyu4i0BeYBWZ5najQajaaKqEjNAaXUYWDymTxIKbVWRPoDzwFPAvs8\n59f9gs0AQjAEozbwC3CDnuOg0Wg0VUuFxEFELsXYO7oJcL9S6rDng79XKfVrWfGVUl8AX5Tir4Cp\nnkOj0Wg01URFluy+AaMvIAG4DqOED9AUmFL5pmk0Go2muqjIJLjpwF+VUgMAu597MlB0rJxGo9Fo\nzlsqIg5tgKXFuJ+g+H0eNBqNRnOeUhFxOEnxM5U7YqzOqtHUaD6c9jc+nPa36jZDozkvqIg4LARe\nEpEGGDOWLZ7ltmcC/z4Xxmk0Go2meqiIODyBMVFtL8bEty3ANxgT456tfNM0Gk1l06NHD8aOHVst\nz05PT0dEauw6XppAKjIJzqGUGoIxq/kujGW0Wyml7vUup6HRaAI5evQoDz/8ME2bNiUoKIiEhARu\nvPFGli4trvuu5pGcnIyIkJ6eXt2maKqYCs1zAFBKpWCsgaTRaEohNTWVq6++moiICJ5//nnat2+P\n2+1m5cqVjB49mn379lU4TafTidlsDtgsCMBut9eYVVY1Fwal1hxE5B8iEuZ3XdrxnIgMFhG9zZRG\nA4wZMwalFOvWreOuu+6iZcuWtG7dmrFjx/Lbb78BsG/fPgYMGEBERAQRERHcfvvtHDhQML5j6tSp\ntG3blnnz5vlqH9nZ2YgIs2fP5vbbbycsLIxJkyYB8Pnnn9OpUyeCg4Np3LgxkydPxm63F2sfGJsL\nXX755URERBATE8PAgQM5ePAgYIibd4/q6OhoRMS3AZFSihkzZtC0aVNCQkK49NJLef/99wPSXrt2\nrc+WDh068Msvv1Ra3mrOPWXVHC4FrH7XpRGEsfx2X+DPZYTVaM6YVfPeIm3v7rIDFiIt1YhzJiOW\nYho1oeewB8od/sSJE3z11Vc888wzhIeHF/GvXbs2Sin69+9PcHAw33zzDSLC2LFj6d+/P2vXrvXV\nDvbs2cPChQv56KOPsNlsBAcHAzBt2jSee+45Zs6ciYiwbNkyhgwZwquvvkq3bt3Yt28fo0ePJj8/\nn5kzZxZrp91uZ9q0abRq1Yr09HQmTpzIoEGDWL16NZdccgmLFi3ijjvuYPPmzdSpU4eQEGPu6xNP\nPMHHH3/M7NmzadmyJT/99BMjR46kdu3a9OvXj+zsbPr160f37t2ZP38+Bw8eDNibQlPzKVUclFI9\ni7suCRHpDKysBLs0mvOaXbt2oZQqdbnxFStW8Ntvv5GSkkJiYiIACxcupFmzZqxcudK334Ldbue9\n994jNjY2IP7dd98dsPvb0KFDeeyxxxg+fDgATZs25cUXX+See+7hpZdeKtIUBXDffff5rps0acIb\nb7xB69atOXDgAA0aNKBOHWMKU0xMjG+r0ezsbF5++WW+/vprrr32WsDYinTNmjXMnj2bfv36sWDB\nAux2O3PnziU8PJy2bdsyefJk7r333opmpaaaqHCfA4CIhAMopbIKef2OrjVozjEVKcH7460x3D3l\nhco0p1jKs0/K1q1biY+P9wkDGB/o+Ph4tmzZ4hOHBg0aFBEGgM6dA5fgX79+PWvWrOHFF1/0ubnd\nbnJzczly5Aj169cvksaGDRuYNm0aGzdu5MSJEz679+3bR4MGDYq1e8uWLeTl5dG3b98AwXE4HL53\n2bp1K+3atQuoNXXt2rWMHNHUJCq68N4jwF/xTIYTkUPAy8ArysAO/K+yjdRozjeaN2+OiLB161YG\nDBhQbBilVLGleSDAPSwsrNgwhd3dbjdTpkxh4MCBRcJGR0cXccvOzqZPnz707t2b9957j5iYGNLT\n07n22mtL7adwu92A0b/RsGHDAD+r1ep7N835TbnFQURmYOzX/BLwk8e5K/AUUB9jMyCNRgPUqVOH\nPn368Nprr/HQQw8V6XfIyMggKSmJgwcPkpqa6itx7969m0OHDpGUlFThZ3bs2JFt27bRrFmzcoXf\ntm0b6enpPPfcczRu3BiAxYsXB4TxjoDy7r8NkJSURFBQEHv37qVXr17Fpp2UlMT8+fPJzs72idjP\nP/9c4XfSVB8VmQQ3AhihlHpWKfWN53gWGAncf27M02jOX15//XWUUnTu3JmPPvqI7du3s23bNt54\n4w3atWtH7969ad++PUOGDGH9+vWsW7eOIUOG0LFjxxI/uqXx1FNPsXDhQp566ik2bdrEtm3b+Pjj\nj5kwofhyW8OGDQkKCuK1115j9+7dfPHFFzz55JMBYRo1aoSI8MUXX3Ds2DGysrKIiIhg/PjxjB8/\nnjlz5rBr1y42btzIv/71L9566y0ABg8ejMVi4b777mPz5s0sX76cZ5/Vc2XPJyoiDmD0KRTnVtF0\nNJoLnsaNG7Nhwwauv/56Jk6cSLt27ejVqxefffYZb775JiLCp59+SnR0ND169KBnz57ExcXx6aef\nltjcVBp9+vThiy++YNWqVXTp0oUuXbrwwgsvFGn68RIdHc38+fP59NNPSUpKYtq0abz88ssBYRIS\nEpg2bRqTJ08mNjbWN7t6+vTpTJ06lZkzZ9KmTRuuv/56Fi1a5KuBhIeHs2TJEnbu3EnHjh0ZP358\nQF+IpuYj5W0bFJFXPOEfLuT+d8CslHqo8s0rP507d1br1q2rThPOW5KTk+nRo0d1m1EspW2MXlHO\nVYd0WZu7a0pH59/ZU1YelvZ3JCLrlVKdC7uX2ucgIv8oFPYeEekDeBsPrwDiMfaS1mhqNFUxSkmj\nuVAozyQ4f9Z7zo085yOeo1VlGqXRaDSa6qXck+AARCQKaI6xZHeKUirj3Jmm0Wg0muqiXB3JItJQ\nRD4HjgO/AGuAdBH5TESK7+3SaDQazXlLmfMcRCQBo4/BjTGnYQsgQBIwBvhJRC5XSh06l4ZqNBqN\npuoozyS4KRib/PRWSuX6uX/iGan0tSfMqHNgn0aj0WiqgfKIw03AkELCAIBSKkdEngDeLxpNo9Fo\nNOcr5elziKb0zX12ecJoNDWatDd/J+3N4uZxajSawpRHHNKA0hZrae4Jo9FoNJoLhPKIw5fAMyIS\nVNhDRIKB6cD5sSGuRnOR06NHD98SGFVNeno6IkJycnK1PP9ckJqaiohwIa7OUB5xmAo0AXaJyEQR\nuU1EbhWRx4GdQFPg6XNoo0Zz3nL06FEefvhh3xafCQkJ3HjjjSxden6Up5KTkxER0tPTq9sU5s2b\nh4j4jtjYWG655RY2b95coXQSExNL3BmvNIoT1ksuuYTDhw9z2WWXVTi9mk6ZHdJKqUMichXwOvAc\nxjBWMCbCLQPGKqUOnjsTNZrzk9TUVK6++moiIiJ4/vnnad++PW63m5UrVzJ69Gj27dtX4TSdTidm\ns7nIwnx2u923vPb5SmpqKo0bNy51L4jQ0FBSUlJQSnHw4EEmTJhAv3792LFjR7W8v9lsJi4ursqf\nWxWUaxKcUipVKXUTUA+40nPUU0rdpJSq+Ga+Gs1FwJgxY1BKsW7dOu666y5atmxJ69atGTt2LL/9\n9htg7Lg2YMAAIiIiiIiI4Pbbb+fAgQO+NKZOnUrbtm2ZN2+er/aRnZ2NiDB79mxuv/12wsLCmDRp\nEmBswNOpUyeCg4Np3LgxkydPLnXjnvfff5/LL7+ciIgIYmJiGDhwIAcPGmW91NRUevY0FkmIjo5G\nRBg2bBhgbOYzY8YMmjZtSkhICJdeeinvvx84aHHt2rU+Wzp06MAvv/xy1nkqIsTFxVG/fn06d+7M\no48+yt69e9m+fbsvzOLFi7n00ksJCgrikksu4dlnn/UJTo8ePdi7dy+PPfaYrwYCcPz4cQYNGkSD\nBg0ICQmhTZs2zJ0715fmsGHD+Pbbb5k9e7YvXmpqarHNSqtXr+aKK64gODiY2NhYHn300YD/gx49\nejBmzBgmTZpEvXr1iImJYfz48b5NlGoKFdoJTil1EmN2tEZTbWR8noL9UHaF4zkOG7vansmIJVt8\nGLVuaVru8CdOnOCrr77imWeeKbLRD0Dt2rVRStG/f3+Cg4P55ptvEBHGjh1L//79Wbt2re/DtWfP\nHhYuXMhHH32EzWYjODgYgGnTpvHcc88xc+ZMRIRly5YxZMgQXn31Vbp168a+ffsYPXo0+fn5JTaj\n2O12pk2bRqtWrUhPT2fixIkMGjSI1atXc8kll7Bo0SLuuOMONm/eTJ06dQgJCQHgiSee4OOPP2b2\n7Nm0bNmSn376iZEjR1K7dm369etHdnY2/fr1o3v37syfP5+DBw/yyCOPVDDXSycjI4OFCxcCBTvQ\nrV+/noEDB/LEE08wZMgQ1q5dy6hRo4iMjOTBBx9k8eLFtG/fnvvuu4+//OUvvrTy8vLo2LEjEydO\nJDIykhUrVjBq1CgaNmzIddddx6uvvsqOHTto1aoVzz33HGAI5v79+wNsOnjwIDfeeCP33nsv8+bN\nIyUlhREjRmAymZg1a5Yv3IIFC3j44Yf58ccf2bhxI4MHD6ZTp04MGjSoUvPobDijPaQ1Gk3p7Nq1\nC6VUqcuNr1ixgt9++42UlBTfTnALFy6kWbNmrFy50reHtN1u57333iuyj/Tdd9/NiBEjfPdDhw7l\nscceY/jw4QA0bdqUF198kXvuuYeXXnqp2D0i7rvvPt91kyZNeOONN2jdujUHDhygQYMG1KlTB4CY\nmBjq1asHGNuLvvzyy3z99ddce+21gLF3xZo1a5g9ezb9+vVjwYIF2O125s6dS3h4OG3btmXy5Mnc\ne++9Fc3KALKzswkPD0cpRU5ODgC33norrVoZa3++/PLLdO/enWnTpgHQokULdu7cyYsvvsiDDz5I\nnTp1MJvNREREBDQHJSQk8Nhjj/nuH3jgAb755hs++OADrrvuOqKiorDZbISGhpbajPT6669Tv359\nXn/9dUwmE61bt+aFF15g1KhRTJ8+ndDQUMDYKe/pp5/22fj222+zcuVKLQ4azdlQkRK8P94aQ8yo\ndpVpTrGUZ5+UrVu3Eh8f7xMGMD7Q8fHxbNmyxScODRo0KCIMAJ07By7Bv379etasWROwqY7b7SY3\nN5cjR45Qv379Imls2LCBadOmsXHjRk6cOOGze9++fTRo0KBYu7ds2UJeXh59+/YNEByHw+F7l61b\nt9KuXbuAWlPXrl2LpOXv7322v9u1117Ll19+6bsPDQ1l48aNOJ1OVq9ezcyZM3nzzTd9/lu3bqVf\nv34Bz7jmmmuYNm0amZmZREZGFvtOLpeLF154gQ8//JCDBw+Sn5+P3W6v8D4nW7dupWvXrphMBS32\n11xzDXa7nV27dtGunfHb8569xMfHk5ZWs2YEaHHQaM4BzZs3R0TYunUrAwYMKDaMUqrEHd/83b17\nMBemsLvb7WbKlCkMHDiwSNjo6KLzVLOzs+nTpw+9e/fmvffeIyYmhvT0dK699tpS+ym8beOff/55\nkV3mvM075d1EbOPGjWRlZREeHs7Bgwfp0aMHGzdu9Pl7m7G8iIhvj+xWrVpx+PBhBg0axKpVq3zP\nLU+eFmbmzJnMmjWLV199lUsvvZTw8HAmTZpU4Q92eZ/vzSd/v/O6z0Gj0ZSPOnXq0KdPH1577TUe\neuihIv0OGRkZJCUlcfDgQVJTU30l7t27d3Po0CGSkpIq/MyOHTuybds238ezLLZt20Z6ejrPPfec\nb3vPxYsXB4TxjgByuVw+t6SkJIKCgti7d2+Je10nJSUxf/58srOzfSL2888/FwnXrFkz3y5mFovF\n51ZeHn30UV5++WUWL17M7bffTlJSEt9//31AmO+//54GDRr4dkqz2WwB7+MNc8stt/iavZRS7Nix\ng1q1agXkReF4xb33f//7X9xut6/28P3332Oz2Wja9MxqvNWF3vtZozlHvP766yil6Ny5Mx999BHb\nt29n27ZtvPHGG7Rr147evXvTvn17hgwZwvr161m3bh1DhgyhY8eOJX50S+Opp55i4cKFPPXUU2za\ntIlt27bx8ccfM2HChGLDN2zYkKCgIF577TV2797NF198wZNPPhkQplGjRogIX3zxBceOHSMrK4uI\niAjGjx/P+PHjmTNnDrt27WLjxo3861//4q233gJg8ODBWCwW7rvvPjZv3szy5ct59tlnK56JZRAZ\nGcmIESOYMmUKbrebcePG8e233zJ16lR27NjBggULmDVrVkAeJCYm8t1333Hw4EHf/I0WLVqwcuVK\nvv/+e7Zt28bYsWPZs2dPwLMSExNZs2YNqamppKenF1vSHzNmDIcOHWLMmDFs3bqVL774gr/97W+M\nHTvW199w3qCUuiCOTp06Kc2ZsWrVquo2oUS2bNlSaWkd/ddv6ui/fqu09LxkZmaW6Hfo0CE1duxY\n1bhxY2Wz2VT9+vVV37591dKlS5VSSu3du1fddtttKjw8XIWHh6v+/fur/fv3++JPmTJFtWnTpki6\ngProo4+KuC9btkxdc801KiQkREVERKhOnTqpf/7znz7/7t27q//7v//z3f/nP/9RTZo0UUFBQery\nyy9XX331lQICfhNPP/20iouLUyKihg4dqpRSyu12q3/84x+qdevWymazqXr16qnevXurr7/+2hfv\n559/Vh06dFA2m021a9dOffbZZ0XS9s+/PXv2KOOTVDxz585VYWFhRdz37t2rLBaLWrBggVJKqUWL\nFqm2bdsqq9WqGjRooJ555hnldrt94X/66SfVrl07FRQU5HveiRMn1IABA1R4eLiKjo5Wjz32mPrL\nX/6iunfv7ou3fft2deWVV6qQkBAFqD179vhsXrt2rS/ct99+q7p06aJsNpuKiYlRjzzyiMrLyyvx\n/0AppYYOHar69etX4ruXRWm/QaVK/zsC1qlivqmiytk2WNPp3LmzuhCnsFcFycnJFe54qypK2xi9\n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\noNFoNJoi/D8ROEf7FPPXVAAAAABJRU5ErkJggg==\n",
            "text/plain": [
              "\u003cFigure size 600x400 with 1 Axes\u003e"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "STOP = 20\n",
        "for quantizer_name, metrics in experiment_kmeans_metrics[\n",
        "    'num_levels=2 seed=47'].items():\n",
        "  if quantizer_name in ('rotated_independent', 'drive'):\n",
        "    continue\n",
        "  x = jnp.mean(metrics['comm_rounds'], axis=0)[:STOP]\n",
        "  y = jnp.mean(metrics['round_obj'], axis=0)[:STOP]\n",
        "  yerr = jnp.std(metrics['round_obj'], axis=0)[:STOP]\n",
        "  plt.errorbar(x, y, yerr, label=quantizer_name)\n",
        "  print(quantizer_name, x[-1], y[-1], yerr[-1])\n",
        "plt.legend()\n",
        "plt.xlabel('Communication Rounds')\n",
        "plt.ylabel('Objective')\n",
        "plt.grid()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "pRUK-rU0eDvr"
      },
      "source": [
        "### Distributed power iteration\n",
        "\n",
        "MNIST (d=784)\n",
        "\n",
        "Compute the top eigenvector of a matrix.\n",
        "In the distributed setting, each client has access to a subset of data.\n",
        "In each iteration, the server broadcasts the current estimate of the eigenvector to all clients.\n",
        "Each client then updates the eigenvector based on one power iteration on its local data, and sends the updated eigenvector back to the server.\n",
        "The server updates the eigenvector by computing the average of the eigenvectors sent by all clients.\n",
        "\n",
        "In the quantized setting, the client compresses the estimated eigenvector before sending to the server.\n",
        "The dataset is distributed over 100 clients."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 61,
      "metadata": {
        "executionInfo": {
          "elapsed": 1,
          "status": "ok",
          "timestamp": 1658265621484,
          "user": {
            "displayName": "",
            "userId": ""
          },
          "user_tz": 240
        },
        "id": "1fRbKXEpj4DC"
      },
      "outputs": [],
      "source": [
        "@jax.jit\n",
        "def single_power_iteration(A, b_k):\n",
        "  b_k1 = jnp.dot(A, b_k)\n",
        "  b_k1_norm = jnp.linalg.norm(b_k1)\n",
        "  return b_k1 / b_k1_norm"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 62,
      "metadata": {
        "executionInfo": {
          "elapsed": 215377,
          "status": "ok",
          "timestamp": 1658265836999,
          "user": {
            "displayName": "",
            "userId": ""
          },
          "user_tz": 240
        },
        "id": "OuIyZB-_eWPy",
        "outputId": "0440c294-67c0-46de-dde1-76aa0e88c3e2"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Average iteration duration = 1.3527563571929933 sec\n",
            "Finished None num_levels=2 seed=47\n",
            "Average iteration duration = 1.8127840995788573 sec\n",
            "Finished Independent num_levels=2 seed=47\n",
            "Average iteration duration = 4.279516196250915 sec\n",
            "Finished Independent+Rotation num_levels=2 seed=47\n",
            "Average iteration duration = 1.7019015550613403 sec\n",
            "Finished TernGrad num_levels=2 seed=47\n",
            "Average iteration duration = 4.591960000991821 sec\n",
            "Finished DRIVE num_levels=2 seed=47\n",
            "Average iteration duration = 2.3791371822357177 sec\n",
            "Finished Correlated num_levels=2 seed=47\n",
            "Average iteration duration = 5.381697010993958 sec\n",
            "Finished Correlated+Rotation num_levels=2 seed=47\n"
          ]
        }
      ],
      "source": [
        "def run_dist_power(x,\n",
        "                   aggregator_name,\n",
        "                   aggregator,\n",
        "                   rng,\n",
        "                   num_clients=100,\n",
        "                   num_rounds=50,\n",
        "                   num_iters=10,\n",
        "                   verbose=True):\n",
        "  start = time.time()\n",
        "  experiment_metrics = {\n",
        "      'eig': [],\n",
        "      'round_l2error': [],\n",
        "      'comm_rounds': [],\n",
        "  }\n",
        "  # Init aggregator state.\n",
        "  aggregator_state = aggregator.init()\n",
        "  for it in range(num_iters + 1):\n",
        "    rng, eig_rng, client_rng = jax.random.split(rng, 3)\n",
        "    # Input to jnp.cov is 2-D array containing multiple variables and observations.\n",
        "    # Each row of represents a variable, and each column a single observation of all\n",
        "    # those variables.\n",
        "    x_cov = jnp.cov(jnp.transpose(x))\n",
        "    # Ground truth top eigenvector.\n",
        "    n = x_cov.shape[0]\n",
        "    _, gt_eig = scipy.linalg.eigh(x_cov, eigvals=(n - 1, n - 1))\n",
        "    gt_eig = jnp.squeeze(gt_eig)\n",
        "    # Init top eigenvector estimate.\n",
        "    eig = jax.random.uniform(eig_rng, shape=(x_cov.shape[1],))\n",
        "    # Split and distribute clients.\n",
        "    clients = partition_data(x, num_clients, client_rng)\n",
        "    # Covariance matrix for each client\n",
        "    clients_covs = [jnp.cov(jnp.transpose(c)) for c in clients]\n",
        "    round_l2error = []\n",
        "    comm_rounds = []\n",
        "    for t in range(num_rounds):\n",
        "      local_eigs = []\n",
        "      local_weights = []\n",
        "      for local_x in clients:\n",
        "        local_x_cov = jnp.cov(jnp.transpose(local_x))\n",
        "        local_eigs.append(single_power_iteration(local_x_cov, eig))\n",
        "        local_weights.append(local_x.shape[0])\n",
        "      eig, aggregator_state = aggregator.apply(\n",
        "          [(j, local_eigs[j], local_weights[j]) for j in range(num_clients)],\n",
        "          aggregator_state)\n",
        "      # L2 distance between current estimated top eigenvector and groundtruth.\n",
        "      l2error = jnp.linalg.norm(gt_eig - eig)\n",
        "      round_l2error.append(l2error)\n",
        "      comm_rounds.append(t + 1)\n",
        "      if t % 10 == 0 and verbose:\n",
        "        print(f'{t} l2error: {round_l2error[t]}')\n",
        "    if it \u003e 0:\n",
        "      experiment_metrics['eig'].append(eig)\n",
        "      experiment_metrics['round_l2error'].append(round_l2error)\n",
        "      experiment_metrics['comm_rounds'].append(comm_rounds)\n",
        "  end = time.time()\n",
        "  print(f'Average iteration duration = {(end - start)/num_iters} sec')\n",
        "  return {k: jnp.array(v) for k, v in experiment_metrics.items()}\n",
        "\n",
        "\n",
        "def run_dist_power_experiment(seed, num_levels_range):\n",
        "  experiment_metrics = {}\n",
        "  cache = {}\n",
        "  for num_levels in num_levels_range:\n",
        "    key = f'num_levels={num_levels} seed={seed}'\n",
        "    experiment_metrics[key] = {}\n",
        "    power_metrics = experiment_metrics[key]\n",
        "    num_clients = 100\n",
        "    rng = jax.random.PRNGKey(seed)\n",
        "    rng, quant_rng = jax.random.split(rng)\n",
        "    quantizers = get_quantizers(\n",
        "        rng=quant_rng,\n",
        "        num_levels=num_levels,\n",
        "        num_clients=num_clients,\n",
        "        thresholds=jnp.array([-1., 1.]),\n",
        "        update_thresholds=True)\n",
        "    for quantizer_name, quantizer in quantizers.items():\n",
        "      if not depends_on_num_levels(quantizer_name) and quantizer_name in cache:\n",
        "        # Does not depend on num_levels and already run so we can safely reuse.\n",
        "        power_metrics[quantizer_name] = cache[quantizer_name]\n",
        "      else:\n",
        "        power_metrics[quantizer_name] = run_dist_power(\n",
        "            mnist_x,\n",
        "            quantizer_name,\n",
        "            quantizer,\n",
        "            rng,\n",
        "            num_clients=num_clients,\n",
        "            num_rounds=20,\n",
        "            num_iters=10,\n",
        "            verbose=False)\n",
        "        cache[quantizer_name] = power_metrics[quantizer_name]\n",
        "      print(f'Finished {quantizer_name} {key}')\n",
        "  return experiment_metrics\n",
        "\n",
        "\n",
        "experiment_power_metrics = run_dist_power_experiment(\n",
        "    seed=47, num_levels_range=[2])"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 63,
      "metadata": {
        "colab": {
          "height": 408
        },
        "executionInfo": {
          "elapsed": 449,
          "status": "ok",
          "timestamp": 1658265837554,
          "user": {
            "displayName": "",
            "userId": ""
          },
          "user_tz": 240
        },
        "id": "IllV4JomzkZW",
        "outputId": "4a5b13f6-f2d1-4d3a-ae4c-b86cd48ed659"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "None 20.0 0.0077334414 0.00033110066\n",
            "Independent 20.0 0.24239223 0.004981057\n",
            "Independent+Rotation 20.0 0.26689598 0.01583763\n",
            "TernGrad 20.0 0.099576086 0.001106607\n",
            "DRIVE 20.0 0.06866177 0.0014224356\n",
            "Correlated 20.0 0.055281162 0.0017702907\n",
            "Correlated+Rotation 20.0 0.059168037 0.0015136597\n"
          ]
        },
        {
          "data": {
            "image/png": "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\ni/8HekSqEgNiK5AAAAAASUVORK5CYII=\n",
            "text/plain": [
              "\u003cFigure size 600x400 with 1 Axes\u003e"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "STOP = 20\n",
        "for quantizer_name, metrics in experiment_power_metrics['num_levels=2 seed=47'].items():\n",
        "  x = jnp.mean(metrics['comm_rounds'], axis=0)[:STOP]\n",
        "  y = jnp.mean(metrics['round_l2error'], axis=0)[:STOP]\n",
        "  yerr = jnp.std(metrics['round_l2error'], axis=0)[:STOP]\n",
        "  plt.errorbar(x, y, yerr, label=quantizer_name)\n",
        "  print(quantizer_name, x[-1], y[-1], yerr[-1])\n",
        "plt.legend()\n",
        "plt.xlabel('Communication Rounds')\n",
        "plt.ylabel('Error')\n",
        "plt.grid()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "A7Zzq863mDFb"
      },
      "source": [
        "## Federated MNIST\n",
        "\n",
        "FedAvg on the federated MNIST dataset."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 64,
      "metadata": {
        "executionInfo": {
          "elapsed": 1706,
          "status": "ok",
          "timestamp": 1658265839412,
          "user": {
            "displayName": "",
            "userId": ""
          },
          "user_tz": 240
        },
        "id": "7ojHnFAYmKTm",
        "outputId": "20bb2680-5167-4c71-db8a-5db951ae3ad8"
      },
      "outputs": [
        {
          "name": "stderr",
          "output_type": "stream",
          "text": [
            "Downloading 'https://storage.googleapis.com/gresearch/fedjax/emnist/federated_emnist_digitsonly_train.sqlite' to '/tmp/.cache/fedjax/federated_emnist_digitsonly_train.sqlite'\n",
            "100%, elapsed: 1s\n",
            "Downloading 'https://storage.googleapis.com/gresearch/fedjax/emnist/federated_emnist_digitsonly_test.sqlite' to '/tmp/.cache/fedjax/federated_emnist_digitsonly_test.sqlite'\n",
            "100%, elapsed: 0s\n"
          ]
        }
      ],
      "source": [
        "train_fd, test_fd = fedjax.datasets.emnist.load_data(only_digits=True)\n",
        "# Hyperparameters for client local traing dataset preparation.\n",
        "client_batch_hparams = fedjax.ShuffleRepeatBatchHParams(batch_size=20)\n",
        "# Just read this once for all experiments.\n",
        "full_test_eval_batches = list(\n",
        "    fedjax.padded_batch_federated_data(\n",
        "        test_fd, fedjax.PaddedBatchHParams(batch_size=256)))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 65,
      "metadata": {
        "executionInfo": {
          "elapsed": 90,
          "status": "ok",
          "timestamp": 1658265839631,
          "user": {
            "displayName": "",
            "userId": ""
          },
          "user_tz": 240
        },
        "id": "kZAIpiRmUGX9"
      },
      "outputs": [],
      "source": [
        "model = fedjax.models.emnist.create_logistic_model(only_digits=True)\n",
        "grad_fn = jax.jit(fedjax.model_grad(model))\n",
        "# Create federated averaging algorithm.\n",
        "client_optimizer = fedjax.optimizers.sgd(learning_rate=0.5)\n",
        "server_optimizer = fedjax.optimizers.sgd(learning_rate=1.0)\n",
        "# For the sake of saving on redundant compile times, I do this outside of this\n",
        "# function for the colab only.\n",
        "with fedjax.for_each_client_backend('pmap'):\n",
        "  train_for_each_client = fedjax.algorithms.fed_avg.create_train_for_each_client(\n",
        "      grad_fn, client_optimizer)\n",
        "\n",
        "ClientId = bytes\n",
        "Grads = fedjax.Params\n",
        "\n",
        "\n",
        "@fedjax.dataclass\n",
        "class ServerState:\n",
        "  \"\"\"State of server passed between rounds.\n",
        "\n",
        "  Attributes:\n",
        "    params: A pytree representing the server model parameters.\n",
        "    opt_state: A pytree representing the server optimizer state.\n",
        "    aggregator_state: Stores the state of the aggregator if non-default\n",
        "      aggregation is needed.\n",
        "    aggregated_delta: Aggregated delta stored purely for diagnostics to check\n",
        "      error of quantization on dist mean estimation.\n",
        "  \"\"\"\n",
        "  params: fedjax.Params\n",
        "  opt_state: fedjax.OptState\n",
        "  aggregator_state: Any\n",
        "  aggregated_delta: fedjax.Params\n",
        "\n",
        "\n",
        "def federated_averaging(\n",
        "    grad_fn: Callable[[fedjax.Params, fedjax.BatchExample, fedjax.PRNGKey],\n",
        "                      Grads],\n",
        "    client_optimizer: fedjax.Optimizer,\n",
        "    server_optimizer: fedjax.Optimizer,\n",
        "    client_batch_hparams: fedjax.ShuffleRepeatBatchHParams,\n",
        "    aggregator: fedjax.aggregators.Aggregator,\n",
        ") -\u003e fedjax.FederatedAlgorithm:\n",
        "  r\"\"\"Builds federated averaging.\n",
        "\n",
        "  Args:\n",
        "    grad_fn: A function from (params, batch_example, rng) to gradients. This can\n",
        "      be created with `fedjax.model_grad`. For FedProx the input is (params,\n",
        "      server_params, batch_example, rng).\n",
        "    client_optimizer: Optimizer for local client training.\n",
        "    server_optimizer: Optimizer for server update.\n",
        "    client_batch_hparams: Hyperparameters for batching client dataset for train.\n",
        "    aggregator: Aggregator that applies any special techniques to process the\n",
        "      client updates, for example quantization, before weighting and averaging.\n",
        "      If none, will use default fedjax.aggregators.mean_aggregator().\n",
        "\n",
        "  Returns:\n",
        "    FederatedAlgorithm\n",
        "  \"\"\"\n",
        "  train_for_each_client = fedjax.algorithms.fed_avg.create_train_for_each_client(\n",
        "      grad_fn, client_optimizer)\n",
        "\n",
        "  def init(params: fedjax.Params) -\u003e ServerState:\n",
        "    \"\"\"Initializes federated averaging server state.\n",
        "\n",
        "    Args:\n",
        "      params: Initial model parameters. This could be randomly initialized with\n",
        "        `fedjax.Model.init` or pre-trained for warm starting.\n",
        "\n",
        "    Returns:\n",
        "      Initial server state.\n",
        "    \"\"\"\n",
        "    opt_state = server_optimizer.init(params)\n",
        "    aggregator_state = aggregator.init()\n",
        "    aggregated_delta = fedjax.tree_util.tree_zeros_like(params)\n",
        "    return ServerState(params, opt_state, aggregator_state, aggregated_delta)\n",
        "\n",
        "  def apply(\n",
        "      server_state: ServerState,\n",
        "      clients: Sequence[Tuple[ClientId, fedjax.ClientDataset, fedjax.PRNGKey]]\n",
        "  ) -\u003e Tuple[ServerState, Mapping[ClientId, Any]]:\n",
        "    \"\"\"Runs one round of federated averaging training.\n",
        "\n",
        "    Args:\n",
        "      server_state: Input server state containing model parameters and optimizer\n",
        "        state.\n",
        "      clients: Clients to train across. Each client has a client id, client\n",
        "        dataset, and initial rng for training.\n",
        "\n",
        "    Returns:\n",
        "      Updated server state and optional per-client diagnostics.\n",
        "    \"\"\"\n",
        "    client_num_examples = {cid: len(cds) for cid, cds, _ in clients}\n",
        "    # [(client_id, batches, client_input), ...].\n",
        "    batch_clients = [(cid, cds.shuffle_repeat_batch(client_batch_hparams), crng)\n",
        "                     for cid, cds, crng in clients]\n",
        "    client_diagnostics = {}\n",
        "    shared_input = server_state.params\n",
        "\n",
        "    # (client ID, params, weights) generator to avoid reading all to memory.\n",
        "    def generate_client_params_and_weights():\n",
        "      for client_id, delta_params in train_for_each_client(\n",
        "          shared_input, batch_clients):\n",
        "        num_examples = client_num_examples[client_id]\n",
        "        # Record diagnostics that aren't necessary for training.\n",
        "        client_diagnostics[client_id] = {}\n",
        "        client_diagnostics[client_id]['delta_l2_norm'] = (\n",
        "            fedjax.tree_util.tree_l2_norm(delta_params))\n",
        "        client_diagnostics[client_id]['delta_minv'] = min(\n",
        "            jnp.min(l) for l in jax.tree_util.tree_leaves(delta_params))\n",
        "        client_diagnostics[client_id]['delta_maxv'] = max(\n",
        "            jnp.max(l) for l in jax.tree_util.tree_leaves(delta_params))\n",
        "        # This is pretty expensive but we do it here for the sake of measuring\n",
        "        # distributed mean estimation error.\n",
        "        client_diagnostics[client_id]['delta_params'] = delta_params\n",
        "        client_diagnostics[client_id]['weight'] = num_examples\n",
        "        yield (client_id, delta_params, num_examples)\n",
        "\n",
        "    client_params_and_weights = generate_client_params_and_weights()\n",
        "    server_state = server_update(server_state, client_params_and_weights)\n",
        "    return server_state, client_diagnostics\n",
        "\n",
        "  def server_update(server_state, client_params_and_weights):\n",
        "    weighted_averaged_delta, new_aggregator_state = aggregator.apply(\n",
        "        client_params_and_weights, server_state.aggregator_state)\n",
        "    opt_state, params = server_optimizer.apply(weighted_averaged_delta,\n",
        "                                               server_state.opt_state,\n",
        "                                               server_state.params)\n",
        "    return ServerState(params, opt_state, new_aggregator_state,\n",
        "                       weighted_averaged_delta)\n",
        "\n",
        "  return fedjax.FederatedAlgorithm(init, apply)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 66,
      "metadata": {
        "executionInfo": {
          "elapsed": 53,
          "status": "ok",
          "timestamp": 1658265839805,
          "user": {
            "displayName": "",
            "userId": ""
          },
          "user_tz": 240
        },
        "id": "nFbHy_DqnFVK"
      },
      "outputs": [],
      "source": [
        "@jax.jit\n",
        "def mean_tree_distance(tree_a, tree_b):\n",
        "  # PyTree of euclidean distances between leaves of tree_a and tree_b. \n",
        "  tree_distance = jax.tree_util.tree_multimap(lambda a, b: jnp.linalg.norm(a - b), tree_a, tree_b)\n",
        "  distance_leaves = jax.tree_util.tree_leaves(tree_distance)\n",
        "  # Average euclidean distance across all leaves.\n",
        "  return sum(distance_leaves) / len(distance_leaves)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 67,
      "metadata": {
        "executionInfo": {
          "elapsed": 2578999,
          "status": "ok",
          "timestamp": 1658268418927,
          "user": {
            "displayName": "",
            "userId": ""
          },
          "user_tz": 240
        },
        "id": "WDWkf_jVc-_W",
        "outputId": "1025d5c2-99ff-466c-834b-bec90c79fc50"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Average round duration = 0.058958380222320556 sec\n",
            "Finished None trial=0\n",
            "Average round duration = 0.06221242523193359 sec\n",
            "Finished Independent trial=0\n",
            "Average round duration = 0.09018512463569642 sec\n",
            "Finished Independent+Rotation trial=0\n",
            "Average round duration = 0.061746336221694946 sec\n",
            "Finished TernGrad trial=0\n",
            "Average round duration = 0.08736959981918335 sec"
          ]
        },
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "\n",
            "Finished DRIVE trial=0\n",
            "Average round duration = 0.07089205336570739 sec\n",
            "Finished Correlated trial=0\n",
            "Average round duration = 0.0930682921409607 sec\n",
            "Finished Correlated+Rotation trial=0\n",
            "Average round duration = 0.057889077663421634 sec\n",
            "Finished None trial=1\n",
            "Average round duration = 0.061073004961013796 sec\n",
            "Finished Independent trial=1\n",
            "Average round duration = 0.08628395962715149 sec\n",
            "Finished Independent+Rotation trial=1\n",
            "Average round duration = 0.06130231928825378 sec\n",
            "Finished TernGrad trial=1\n",
            "Average round duration = 0.08800958704948425 sec\n",
            "Finished DRIVE trial=1\n",
            "Average round duration = 0.06611519384384155 sec\n",
            "Finished Correlated trial=1\n",
            "Average round duration = 0.09171519947052002 sec\n",
            "Finished Correlated+Rotation trial=1\n",
            "Average round duration = 0.0588238091468811 sec\n",
            "Finished None trial=2\n",
            "Average round duration = 0.06182750654220581 sec\n",
            "Finished Independent trial=2\n",
            "Average round duration = 0.08907822632789612 sec\n",
            "Finished Independent+Rotation trial=2\n",
            "Average round duration = 0.06151041221618652 sec\n",
            "Finished TernGrad trial=2\n",
            "Average round duration = 0.08958758497238159 sec\n",
            "Finished DRIVE trial=2\n",
            "Average round duration = 0.0677996907234192 sec\n",
            "Finished Correlated trial=2\n",
            "Average round duration = 0.0926286826133728 sec\n",
            "Finished Correlated+Rotation trial=2\n",
            "Average round duration = 0.05718286085128784 sec\n",
            "Finished None trial=3\n",
            "Average round duration = 0.06083348536491394 sec\n",
            "Finished Independent trial=3\n",
            "Average round duration = 0.08506827139854431 sec\n",
            "Finished Independent+Rotation trial=3\n",
            "Average round duration = 0.0594928035736084 sec\n",
            "Finished TernGrad trial=3\n",
            "Average round duration = 0.08544393682479859 sec\n",
            "Finished DRIVE trial=3\n",
            "Average round duration = 0.06665888094902038 sec\n",
            "Finished Correlated trial=3\n",
            "Average round duration = 0.09420446920394897 sec\n",
            "Finished Correlated+Rotation trial=3\n",
            "Average round duration = 0.057541919708251954 sec\n",
            "Finished None trial=4\n",
            "Average round duration = 0.06128565144538879 sec\n",
            "Finished Independent trial=4\n",
            "Average round duration = 0.08839110159873963 sec\n",
            "Finished Independent+Rotation trial=4\n",
            "Average round duration = 0.060538044452667236 sec\n",
            "Finished TernGrad trial=4\n",
            "Average round duration = 0.08554350328445434 sec\n",
            "Finished DRIVE trial=4\n",
            "Average round duration = 0.06685895872116089 sec\n",
            "Finished Correlated trial=4\n",
            "Average round duration = 0.09099580836296081 sec\n",
            "Finished Correlated+Rotation trial=4\n"
          ]
        }
      ],
      "source": [
        "def run_fedavg(train_fd,\n",
        "               test_fd,\n",
        "               model,\n",
        "               algorithm,\n",
        "               rng,\n",
        "               sample_clients_seed=0,\n",
        "               num_clients=10,\n",
        "               num_rounds=2000,\n",
        "               init_thresholds=None,\n",
        "               verbose=True):\n",
        "  # Ground truth mean aggregator\n",
        "  gt_mean_aggregator = fedjax.aggregators.mean_aggregator()\n",
        "  gt_mean_state = gt_mean_aggregator.init()\n",
        "  # Initialization.\n",
        "  rng, model_rng = jax.random.split(rng)\n",
        "  init_params = model.init(model_rng)\n",
        "  server_state = algorithm.init(init_params)\n",
        "  if init_thresholds is not None:\n",
        "    # Set correlated thresholds according to init if it's set.\n",
        "    aggregator_state = server_state.aggregator_state.replace(\n",
        "        thresholds=init_thresholds)\n",
        "    server_state = server_state.replace(aggregator_state=aggregator_state)\n",
        "  round_full_test_loss = []\n",
        "  round_full_test_acc = []\n",
        "  round_dme_error = []\n",
        "  round_delta_minv = []\n",
        "  round_delta_maxv = []\n",
        "  comm_rounds = []\n",
        "  # Train and eval loop.\n",
        "  train_client_sampler = fedjax.client_samplers.UniformGetClientSampler(\n",
        "      fd=train_fd, num_clients=num_clients, seed=sample_clients_seed)\n",
        "  start = time.time()\n",
        "  for t in range(num_rounds):\n",
        "    # Sample 10 clients per round without replacement for training.\n",
        "    clients = train_client_sampler.sample()\n",
        "    # Run one round of training on sampled clients.\n",
        "    server_state, client_diagnostics = algorithm.apply(server_state, clients)\n",
        "    if t % 5 == 0:\n",
        "      # Check error of mean estimation to ground truth mean aggregator.\n",
        "      gt_mean_delta, gt_mean_state = gt_mean_aggregator.apply(\n",
        "          [(cid, cd['delta_params'], cd['weight'])\n",
        "           for cid, cd in client_diagnostics.items()], gt_mean_state)\n",
        "      # Periodically evaluate the trained server model parameters.\n",
        "      full_test_metrics = fedjax.evaluate_model(model, server_state.params,\n",
        "                                                full_test_eval_batches)\n",
        "      round_full_test_loss.append(full_test_metrics['loss'])\n",
        "      round_full_test_acc.append(full_test_metrics['accuracy'])\n",
        "      round_dme_error.append(\n",
        "          mean_tree_distance(gt_mean_delta, server_state.aggregated_delta))\n",
        "      round_delta_minv.append(\n",
        "          min(d['delta_minv'] for d in client_diagnostics.values()))\n",
        "      round_delta_maxv.append(\n",
        "          max(d['delta_maxv'] for d in client_diagnostics.values()))\n",
        "      comm_rounds.append(t + 1)\n",
        "      if verbose:\n",
        "        print(f'[{t}] {round_metrics[t]}')\n",
        "  end = time.time()\n",
        "  print(f'Average round duration = {(end - start) / num_rounds} sec')\n",
        "  return {\n",
        "      'round_full_test_loss': round_full_test_loss,\n",
        "      'round_full_test_acc': round_full_test_acc,\n",
        "      'round_dme_error': round_dme_error,\n",
        "      'round_delta_minv': round_delta_minv,\n",
        "      'round_delta_maxv': round_delta_maxv,\n",
        "      'comm_rounds': comm_rounds,\n",
        "      'server_state': server_state,\n",
        "  }\n",
        "\n",
        "\n",
        "def run_fedavg_experiment(seed, num_levels, num_iters):\n",
        "  # Logic to avoid redundant experiments for diff num_levels.\n",
        "  rng = jax.random.PRNGKey(seed)\n",
        "  experiment_metrics = []\n",
        "  for trial in range(num_iters):\n",
        "    rng, use_rng = jax.random.split(rng)\n",
        "    fedavg_metrics = {}\n",
        "    use_rng, quant_rng = jax.random.split(use_rng)\n",
        "    num_clients = 10\n",
        "    quantizers = get_quantizers(\n",
        "        rng=quant_rng,\n",
        "        num_levels=num_levels,\n",
        "        num_clients=num_clients,\n",
        "        thresholds=jax.tree_util.tree_map(lambda l: jnp.array([-1.0, 1.0]),\n",
        "                                          model.init(jax.random.PRNGKey(0))),\n",
        "        update_thresholds=True)\n",
        "    for quantizer_name, quantizer in quantizers.items():\n",
        "      algorithm = federated_averaging(grad_fn, client_optimizer,\n",
        "                                      server_optimizer,\n",
        "                                      client_batch_hparams, quantizer)\n",
        "      init_thresholds = None\n",
        "      if 'correlated' in quantizer_name:\n",
        "        # Initialize thresholds for correlated with couple of rounds first.\n",
        "        s = run_fedavg(\n",
        "            train_fd,\n",
        "            test_fd,\n",
        "            model,\n",
        "            algorithm,\n",
        "            use_rng,\n",
        "            sample_clients_seed=0,\n",
        "            num_clients=num_clients,\n",
        "            num_rounds=20,\n",
        "            verbose=False)['server_state']\n",
        "        init_thresholds = s.aggregator_state.thresholds\n",
        "      fedavg_metrics[quantizer_name] = run_fedavg(\n",
        "          train_fd,\n",
        "          test_fd,\n",
        "          model,\n",
        "          algorithm,\n",
        "          use_rng,\n",
        "          sample_clients_seed=0,\n",
        "          num_clients=num_clients,\n",
        "          num_rounds=1000,\n",
        "          init_thresholds=init_thresholds,\n",
        "          verbose=False)\n",
        "      print(f'Finished {quantizer_name} trial={trial}')\n",
        "    experiment_metrics.append(fedavg_metrics)\n",
        "  return experiment_metrics\n",
        "\n",
        "\n",
        "experiment_fedavg_metrics = run_fedavg_experiment(\n",
        "    seed=47, num_levels=2, num_iters=5)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 68,
      "metadata": {
        "executionInfo": {
          "elapsed": 6103,
          "status": "ok",
          "timestamp": 1658268425172,
          "user": {
            "displayName": "",
            "userId": ""
          },
          "user_tz": 240
        },
        "id": "6Ka4x-sNzn2N"
      },
      "outputs": [],
      "source": [
        "t = {}\n",
        "for d in experiment_fedavg_metrics:\n",
        "  for k, v in d.items():\n",
        "    if k not in t:\n",
        "      t[k] = {}\n",
        "    for ki, vi in v.items():\n",
        "      if ki not in t[k]:\n",
        "        t[k][ki] = []\n",
        "      t[k][ki].append(vi)\n",
        "\n",
        "for d in experiment_fedavg_metrics:\n",
        "  for k, v in d.items():\n",
        "    for ki, vi in v.items():\n",
        "      if ki != 'server_state':\n",
        "        t[k][ki] = jnp.array(t[k][ki])\n",
        "\n",
        "experiment_fedavg_metrics = t"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 69,
      "metadata": {
        "colab": {
          "height": 414
        },
        "executionInfo": {
          "elapsed": 1447,
          "status": "ok",
          "timestamp": 1658268426777,
          "user": {
            "displayName": "",
            "userId": ""
          },
          "user_tz": 240
        },
        "id": "55sfSaB1viia",
        "outputId": "0c5402c6-8609-45ca-868e-8bdb189ee1ce"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "None 99600.0 89.29712 0.0161266\n",
            "Independent 99600.0 87.39959 0.32088867\n",
            "Independent+Rotation 99600.0 89.10756 0.081427574\n",
            "TernGrad 99600.0 88.47179 0.015206117\n",
            "DRIVE 99600.0 89.2932 0.008108033\n",
            "Correlated 99600.0 88.38264 0.084860906\n",
            "Correlated+Rotation 99600.0 89.22169 0.04731172\n"
          ]
        },
        {
          "data": {
            "image/png": "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\nM6YanovKlUGVlYwQQgv8DxgK3AL8B0wHvr0wol05yNxiwIMSZyHBOoHmLE0bKiqn48033+SFF15g\nyBDlENo6deowceJE7rvvPt599123IujUqROjRo1y11u3bh0Oh4OpU6fSoEEDAEaOHMmQIUNwuVxo\nNBr2799f7viGgQMH8ssvvwDK2fC7du1y50VHR/Pee++VKf/ss8+6/46KimLSpEn06dOHuXPnotFo\nmDJlCt26dWPs2LGAcpDapk2bmDlzZvU8IJXLmjMqGSFEAxTF8gBQCCxA2R9zv5Ry94UV78rAo8QF\nOih2FODp53mpxVGpgHOdURy+/wEcTie15l2682S2bNnCxo0bmTjxRJhAl8tFcXExKSkphIWFAdC6\ndfndBEaj0a1gAMLDw7Hb7eTk5ODv71/h/T744ANGjx7NokWLyh2U1apV+Zh8q1at4u2332bPnj3k\n5ubidDqx2WykpKQQHh7Onj17uP3228vUadeunapkrhFOq2SEEH8BTYDFQH8p5Z+l6aMvgmxXDBa7\nFnRgdeTjGaTu9FepXlwuF+PGjePuu8sf6RQUFOT++/gpliej05X9iR+f9bhcLkCZVezdW9aMGBoa\niqenJwEVRK049R6HDx+mV69ePPLII7z22msEBASwdetWBg4c6PYZVeVgRJWrlzPNZNoBU4EZUsqd\nF0GeKw6n3YVF6pW/rXkY6kVeYolUrjZatmzJ3r173WfGVycDBw5kzJgxbN68ucKZ0JnYvHkzNpuN\nDz74AK1Wcc0uWbKkTJmYmBj+/vvvMmmnXqtcvZxJybQGHgH+EkIkAF8C1XdY9lVAWlohJg24pAtp\nzUNfs/2lFknlKuPVV1+ld+/e1KpVi/79+6PT6di5cycbN25k0qTzC4I+YsQIli1bRvfu3Rk3bhwd\nOnQgICCA//77j59++smtOCqjXr16uFwuPvzwQ/r27cvff/9dbsnz008/zY033sjbb7/NXXfdRWxs\nLD/88MN5ya1y5XDaJcxSym1SyieBMOB9oA+QWFqvlxDC78KLeHmTmlWMSQisriL0Dgf66AZnrqSi\nchb06NGDpUuXsnr1atq2bUvbtm155513qFkNkb6NRiMrVqxg7NixfPnll3Ts2JGGDRsycuRIWrRo\nwR9//HHa+k2bNmXKlCm8//77xMTE8MUXX5RZ8QZwww03MHPmTKZPn07Tpk35/vvvGT9+/HnLrnJl\n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\n",
            "text/plain": [
              "\u003cFigure size 600x400 with 1 Axes\u003e"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "# STOP * 5 is the round number it will stop on.\n",
        "STOP = 201\n",
        "for quantizer_name, metrics in experiment_fedavg_metrics.items():\n",
        "  x = jnp.mean(metrics['comm_rounds'], axis=0)[:STOP]\n",
        "  y = jnp.mean(metrics['round_full_test_acc'], axis=0)[:STOP]\n",
        "  yerr = jnp.std(metrics['round_full_test_acc'], axis=0)[:STOP]\n",
        "  plt.errorbar(x, y, yerr, label=quantizer_name)\n",
        "  print(quantizer_name, x[-1] * 100, y[-1] * 100, yerr[-1] * 100)\n",
        "plt.legend()\n",
        "plt.xlabel('Communication Rounds')\n",
        "plt.ylabel('Accuracy')\n",
        "plt.ylim((0.7, 0.9))\n",
        "plt.grid()"
      ]
    }
  ],
  "metadata": {
    "colab": {
      "collapsed_sections": [],
      "last_runtime": {
        "build_target": "//learning/deepmind/public/tools/ml_python:ml_notebook",
        "kind": "private"
      },
      "name": "Correlated compression experiments",
      "provenance": [
        {
          "file_id": "1_QXVpn3Wp0zTM5_YqJC_v899Cj2av8H0",
          "timestamp": 1654117730178
        }
      ],
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    },
    "language_info": {
      "name": "python"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
